I've watched a lot of youtube tutorials on a lot of different topics (not just programming related). Doubt you will see this, but I just gotta say this is some of the best written, edited, and paced educational content I've ever seen. You are extremely talented! Just the right speed, everything is super clear - Wish there was more content of this quality on youtube.
This video is just brilliant, i had so many ah huh moments watching please do more. I love this video specifically because it leads be towards understanding what is happening. Brilliant
Incredible video, I'm a maintainer of Hurl, an Open source project and just have an issue about major version breaking change. One solution proposed is to implement the "Pattern builder", so here we go, I just have to follow the video !!! Thanks, crystal clear, subscribed!
Thanks, upcoming video about TypeState in Rust, and how to combine it with the builder pattern. Basically, makes the .url check compiled time v.s runtime. If you go to the github repo, the code is already there. Happy coding!
I'm a mid-lvl PHP dev and my company as recently been bought and I found myself having to learn Rust now. This video on the Builder Pattern is exactly the kind of video you don't find anywhere else I feel very lucky to have find surch gems! please do continue ! would love to hear about other patterns and code standar for Rust. Thanks a lot for sharing your precious knoledge
i just want to point out that there is a derive_builder crate, which removes quite a bit of the boilerplate on complex structs. Thanks for the video, great stuff!
i was thinking just the other day that i would like a book on design patterns implemented in rust, then the youtube algorithm recommends you, not bad at all
Great video! I like destructuring the builder on the first line of the build function, it avoids all the "self." and feels cleaner to me. It might not make sense for really short builder functions though. let Self { /* builder field names */ } = self;
Great video! Just a question: is there any reason why you don't use the newtype pattern `struct RequestBuilder(Request);` instead of copying all the fields in a new struct? I mean I understand that you loose a little bit of flexibility because now the fields are not Option's (unless they are in the constructed type) and in your example you would have to check if the url is empty - but I think that's also fine. I would anyway argue that when something isn't optional it's better to add it as a parameter to new, and then you also don't need to return a Result, because it can't fail. I just feel that with newtype it's clearer, there is way less repetition and code and you work on the constructed object directly.
I'm new to Rust and I wonder if you can create a builder class of a struct by just adding an annotated macro. I see this in other languages and can't help to stop thinking in non-rust ways of doing things. I this is not good advice, I'd like to know why. Thanks!
Yes, some crates allow you to annotate your struct to provide a builder implementation. One popular option is derive_builder: crates.io/crates/derive_builder.
I was hoping that you would take it a step further with the consuming builder and use type state to ensure that the request has an url, it would get rid of the result on the build method; making the API infallible if it compiles!
In the meantime, here is a great link about State Builder Pattern: www.greyblake.com/blog/builder-with-typestate-in-rust/ I will add it to the video description.
Thanks to your suggestion, I did the video about the TypesState Builder pattern: th-cam.com/video/pwmIQzLuYl0/w-d-xo.html Interesting enough, somehow, after explaining everything, I forgot to mention in the video that now build() can just return Request rather than Result of Request. But I added it in the description after someone told me. Anyway, thanks for the feedback!
Thanks! Before, I didn't really understand the point of the builder pattern. Your explanation helped. At 5:54, you had "String" selected, and then it suddenly became "Option". Do you have some special keyboard shortcut that wraps the selected text in "Option"?
About the String to Option, that was a video editing trick. Now, there is something in VSCode/RustAnalyzer. If you select "String" and go to refactor, then, you have an option "Surround with Option". There should be a way to make it a shortcut. We could also do a surround snippet. I did not do any of those yet, I might at some point.
Thank you for making this video, it's really clear and informative. One thing I noticed: when I changed `self.url = Some(url.into())` to `self.url.insert(url.into())`, clippy complained about the Unused Return Value (from Option::insert()) - so I prefix with `let _ = `, but that seems inelegant. Is there a nicer way to use insert without triggering clippy?
Hi thanks for your effort. I watched your video on ownership and jumped to this video. Its really hard for me to follow up. Can you suggest the order I should follow in watching your videos to understand this video. I mean what videos I should watch before watching this?
Very interesting and timely. Thank you again. I'm trying to learn "iced" while learning rust. The language is confusing enough, but I am determined. In the first example supplied from the iced site, the code does not compile. With the help of the compiler, I was able to fix all but one error. This error is based on the use of the .into() function. I cannot figure it out. Would it be possible for you to do a video on "iced?" If not, can I send you the short code? I come from years of using Delphi (Pascal) so a lot of this is quite understandable, but some things are really difficult. I need a GUI so I can make the kinds of applications people ask me for. Merci beaucoup pour votre temps!
I really love these videos, going through quite a few of yours! The only thing that keeps bothering me is that your editor hides code, leaving us, with normal editors that don't auto-import missing dependencies in the dark (a bit) about what's needed. I wish you would always display the full code and instead perhaps highlight, or at least show the full code, then hide/omit it while continuing on it. Thank you for making these lovely videos!
Thank you for your feedback. Are you saying you don't have the small pop-up that displays the import? The difficulty is that I can't show everything. In the upcoming videos, I'm planning to pause a bit more when a decision needs to be made, but will still present the obvious ones briefly. I'm doing this with the pop-up feature in mind, as I assume most editors come equipped with this functionality. Is that a fair assumption?
That is an excellent and timely question. The Awesome-App series will be about this in a way, but more from an entire application point of view. The first video was an introduction, th-cam.com/video/BY_ZjPGqJJk/w-d-xo.html, but I plan to make enhancements as videos, and some of them will include API designs. One of those enhancements I am working on is a utility ModQL (for Model Query Language), which I might make a "rebuild from scratch" video if I find a way to make it not too overwhelming. However, this would be a great lib tutorial as it uses a lot of patterns, including procedural drive macros and Serde deserialization implementation. For now, ModQL (crates.io/crates/modql) implementation is still experimental, but the concept/design (.e.g., joql - joql.org/) is something we use in our production applications, so it is relatively mature and value-tested. The challenge is how much context and information I can put in a video before it becomes overwhelming and confusing. In my previous video, I found I could go up to 500 to 1k loc without getting too confusing. The catch is that ModQL might need a lot of contexts, as it follows the JSON-RPC model over REST, and could be seen as an alternative to GraphQL, and mistaken for an ORM. Now, I am all ears if you have a more straightforward API suggestion.
Good question. It is because the Result type is aliased in the prelude.rs. This is a common pattern in Rust. It allows normalizing all of the Results of your crate. So, in the prelude.rs, I have // re-export the crate Error pub use crate::error::Error; // alias Result to be the crate Result pub type Result = core::result::Result; And because I typically import the prelude use crate::prelude::* Then, I can just use this Result type alias. Here the video about the code layout I use : th-cam.com/video/oxx7MmN4Ib0/w-d-xo.html
Agree, 1) non consuming pattern .build(&self) is ok, but mostly when the builder data are not directly used for building the object (so, no implicit clone, because no clone is needed). 2) non consuming pattern with .build(&mut self) is bad IMO. I do not see why that would be a good idea. 3) The consuming pattern, with build(self) is the one most aligned with Rust IMO, and my default go to when I need a builder pattern.
That depends. Does the clone happen only once or is it called several times? If there's only one clone when doing thousands or millions of operations, it is okay. But if clone happens several times during the runtime, then it might slow the CPU by a lot. Creating and copying memory is extremely slow in comparison to just CPU calculations.
Hope this won't come off as rude but, have you considered doing subtitles for your content? It's probably because I am just a beginner so I'm not familiar with all of the lingo (and also don't have any friends who speak with your specific accent) , but there are times when I just cannot understand a word or sentence of your narration.
I think TH-cam does the auto CC, and it is not too bad. Try to press the CC icon somewhere. Sometime I have to correct couple of words, but overall it should be ok.
One day I might, but I am in a catch-22 situation. I still have my airport accent when I speak English, but it's tough for me to talk tech in French (I have been living in San Francisco most of my tech life). I will give it some thought, though. My planned 2023 Full Rust Course could be a good candidate for a VF.
The build pattern is one of the Achilles' heel of Rust because of the borrow checker. The "-> &mut self" denies the fluent method and the "-> Self" denies later addition before doing the build without reassigning, so there's no escaping boilerplate code. I would definitely avoid to copy all the fields in the builder though, that's unnecessary. Instead, only add one field with the target object (Request) which is preset to default, it will simplify a lot. You can hand it over in the build() method without overhead.
@@diadetediotedio6918 What do you mean by "literally"? And for which part? Generally speaking, I don't think macros are the solution to every shortcoming, you would just have macro boilerplate code instead, that you would need to include in each crate. I'd rather fancy a way to tell the compiler that when an object is not actually moved in a function that takes ownership, it can be considered as still owned by the original owner. Or maybe that's something the compiler will be able to work out on its own one day. In this case, the setters could benefit from it, but I've seen this annoying pattern in other situations.
I would differ. I think the Rust data model suits the builder pattern very well, especially for the consuming builder pattern. The Rust mutable model makes them thread-safe and efficient while making cloning explicit. The builder re-assignment is a minor cosmetic oddity but a totally logical one. Now, I would agree that the Builder Pattern should not be abused. It should be an exception for those "structures" that would gain to be built in steps or are relatively complex. Good examples of well-thought-out are Bevy, Tauri plugins, Clap command tree, and many Web framework routing/service structures. SQL Builders are an excellent fit for this pattern as well. I used to avoid builder patterns in Java, but now, in Rust, when appropriate, I find them very productive. The TypeState builder pattern can also be powerful and relatively natural in Rust (I remember in my Java days having issues trying to chain methods that return variation of the self type). TypeState Builder Pattern video coming soon...
The re-assignment and the other ways to work around the borrow checker make the code noisier, but you're right, it's not a blocking issue. Hopefully it will get smarter with time when ownership transfer in functions can be detected by the way they are used. I think build patterns are hard to avoid especially in Rust because there are not many alternatives if you want to avoid mutable fields. Another good pattern is optional named arguments: in languages like Kotlin or Python for example, they are a huge benefit in that regard. That's another feature Rust would greatly benefit from.
Does the CC does not show for you? YoutTube auto caption is quite impressive with accents. I reviewed it, and seems to be ok. Let me know if the CC does not work for you.
@@JeremyChone At 2:32, you said "trait", but the CC said "trade". That's minor. Slightly more difficult is that the CC text obscures the bottom of the video. The alpha value for the CC text is a little less than 1.0, which makes it a little harder to read when there is other text underneath. If only TH-cam would make the content taller so that the CC text appears beneath the video... I ended up turning CC off, and then using the keyboard shortcut to jump back 10 seconds if I needed to hear something repeated. Slowing playback speed to 75% is sometimes helpful. I think what you're doing is fine. I have several alternatives to help me if I'm having a hard time understanding.
@@markday3145 First, thanks for the "trade" CC miss. I missed it in my quick review. I just fixed it. Second, thanks for the detailed and constructive feedback. I will see what I can do for the CC location, but it is hard to factor this in and have an optimum code layout. But I get your point. The accent and mispronunciation are bugs I have tried to fix for a while, but it is hard to fix something you can't see (i.e., hear). I will work on enunciating better (it might take some time). Now the good news is that it has been said that people that listen to me speak English end up understanding French better. So, time to plan a Paris vacation!
@@JeremyChone I assume you're a non-native English speaker. In that case, I think your English is actually pretty good. Your accent is... you; and it's not that hard for me, either. I've got tinnitus and sensory processing disorder. Both make it more challenging for me understand human speech in general, and especially in videos. Dialogue in videos is harder if I'm trying to concentrate on both the visual and the audio at the same time. Not speaking while the visuals are changing helps, but I'm not asking you to try to accommodate that. Being able to jump back 10 or 30 seconds, or to slow down playback, both go a long way to making it easier for me.
I love the new style with the multiple windows
Good to know. Thanks for the note.
yeah.. its really awesome.. very easy to understand the point. but surely it takes much more effort.
I've watched a lot of youtube tutorials on a lot of different topics (not just programming related). Doubt you will see this, but I just gotta say this is some of the best written, edited, and paced educational content I've ever seen. You are extremely talented! Just the right speed, everything is super clear - Wish there was more content of this quality on youtube.
jam packed with information. this 20 minutes is packed with years of experience in rust.
I just love the way you explain concepts nicely, clearly and progressively. I hope to see more Rust videos like this.
This video is just brilliant, i had so many ah huh moments watching please do more. I love this video specifically because it leads be towards understanding what is happening. Brilliant
Incredible video, I'm a maintainer of Hurl, an Open source project and just have an issue about major version breaking change. One solution proposed is to implement the "Pattern builder", so here we go, I just have to follow the video !!! Thanks, crystal clear, subscribed!
Thanks, upcoming video about TypeState in Rust, and how to combine it with the builder pattern. Basically, makes the .url check compiled time v.s runtime. If you go to the github repo, the code is already there.
Happy coding!
Awesome, the multiple windows style is great, thank you
I'm a mid-lvl PHP dev and my company as recently been bought and I found myself having to learn Rust now.
This video on the Builder Pattern is exactly the kind of video you don't find anywhere else I feel very lucky to have find surch gems!
please do continue ! would love to hear about other patterns and code standar for Rust.
Thanks a lot for sharing your precious knoledge
Dam I learned a lot, not just about builder patterns.
Well done!
Awesome tutorial. Rust is just addictive. Keep it up Jeremy. 👏
i just want to point out that there is a derive_builder crate, which removes quite a bit of the boilerplate on complex structs. Thanks for the video, great stuff!
These tutorials are just perfect
Your videos are so well constructed and very informative - Thank you!
Eyyy, thanks for this one glad that you got time to do this one. Superbly done Jeremy.
Thanks. This one was from your suggestion.
Very well made, I learned a lot, thank you
Dude, thanks for bringing this quality of rust tutorial, your content is very good and straightforward
Great example, so far the best video on Builder Patterns on Rust.
Thanks. Builder with type state coming soon!
today I learned about the `insert` method on the Option enum. that will clean up a bit of my code..
i was thinking just the other day that i would like a book on design patterns implemented in rust, then the youtube algorithm recommends you, not bad at all
I love your videos. you have taught me so me great little rust tricks and patterns. Keep up the great work!
Thanks for the new video @jeremy
i love this reminder
I tend to use '&mut self' for all the configuration methods and then use 'self' on the build method to consume it finally.
That would work. But the catch is that we cannot chain all the way to the build, right?
Great video! I like destructuring the builder on the first line of the build function, it avoids all the "self." and feels cleaner to me. It might not make sense for really short builder functions though.
let Self { /* builder field names */ } = self;
Interesting. So, you put the builder properties public, right?
Thanks Jeremy, great job 👍
I can only agree with the other comments! The quality is amazing, with the overlaying windows! Love it! keep up the good work!
Pure gold.
I really need a quick vid explaining clone cloned take as_ref etc etc
Nice video, idk what is the most impressive between you're lvl in Rust or Vscode 😅
Great video!
Just a question: is there any reason why you don't use the newtype pattern `struct RequestBuilder(Request);` instead of copying all the fields in a new struct?
I mean I understand that you loose a little bit of flexibility because now the fields are not Option's (unless they are in the constructed type) and in your example you would have to check if the url is empty - but I think that's also fine. I would anyway argue that when something isn't optional it's better to add it as a parameter to new, and then you also don't need to return a Result, because it can't fail.
I just feel that with newtype it's clearer, there is way less repetition and code and you work on the constructed object directly.
I'm new to Rust and I wonder if you can create a builder class of a struct by just adding an annotated macro. I see this in other languages and can't help to stop thinking in non-rust ways of doing things. I this is not good advice, I'd like to know why. Thanks!
Yes, some crates allow you to annotate your struct to provide a builder implementation.
One popular option is derive_builder: crates.io/crates/derive_builder.
@@JeremyChone thanks for the reply. Ill take a look. Keep on with the great content
I was hoping that you would take it a step further with the consuming builder and use type state to ensure that the request has an url, it would get rid of the result on the build method; making the API infallible if it compiles!
Yes, very good point. Somehow I forgot about it.
I might do another video at some point.
Thanks for this great feedback!
In the meantime, here is a great link about State Builder Pattern: www.greyblake.com/blog/builder-with-typestate-in-rust/
I will add it to the video description.
Thanks to your suggestion, I did the video about the TypesState Builder pattern: th-cam.com/video/pwmIQzLuYl0/w-d-xo.html
Interesting enough, somehow, after explaining everything, I forgot to mention in the video that now build() can just return Request rather than Result of Request. But I added it in the description after someone told me.
Anyway, thanks for the feedback!
Thanks! Before, I didn't really understand the point of the builder pattern. Your explanation helped.
At 5:54, you had "String" selected, and then it suddenly became "Option". Do you have some special keyboard shortcut that wraps the selected text in "Option"?
About the String to Option, that was a video editing trick.
Now, there is something in VSCode/RustAnalyzer.
If you select "String" and go to refactor, then, you have an option "Surround with Option". There should be a way to make it a shortcut.
We could also do a surround snippet. I did not do any of those yet, I might at some point.
Thank you for making this video, it's really clear and informative.
One thing I noticed: when I changed `self.url = Some(url.into())` to `self.url.insert(url.into())`, clippy complained about the Unused Return Value (from Option::insert()) - so I prefix with `let _ = `, but that seems inelegant. Is there a nicer way to use insert without triggering clippy?
Hi thanks for your effort. I watched your video on ownership and jumped to this video. Its really hard for me to follow up. Can you suggest the order I should follow in watching your videos to understand this video. I mean what videos I should watch before watching this?
Very interesting and timely. Thank you again.
I'm trying to learn "iced" while learning rust. The language is confusing enough, but I am determined. In the first example supplied from the iced site, the code does not compile. With the help of the compiler, I was able to fix all but one error. This error is based on the use of the .into() function. I cannot figure it out.
Would it be possible for you to do a video on "iced?" If not, can I send you the short code?
I come from years of using Delphi (Pascal) so a lot of this is quite understandable, but some things are really difficult. I need a GUI so I can make the kinds of applications people ask me for.
Merci beaucoup pour votre temps!
Thank you for the kind words. Iced is an interesting UI technology, but for now, we are focusing Tauri and UIWebView/TypeScript for frontends.
is there a good way to make it mandatory to call .url() before build()?
I really love these videos, going through quite a few of yours! The only thing that keeps bothering me is that your editor hides code, leaving us, with normal editors that don't auto-import missing dependencies in the dark (a bit) about what's needed. I wish you would always display the full code and instead perhaps highlight, or at least show the full code, then hide/omit it while continuing on it.
Thank you for making these lovely videos!
Thank you for your feedback.
Are you saying you don't have the small pop-up that displays the import? The difficulty is that I can't show everything. In the upcoming videos, I'm planning to pause a bit more when a decision needs to be made, but will still present the obvious ones briefly. I'm doing this with the pop-up feature in mind, as I assume most editors come equipped with this functionality.
Is that a fair assumption?
Have you considered making a longer video with more comprehensive detail for API building?
That is an excellent and timely question.
The Awesome-App series will be about this in a way, but more from an entire application point of view. The first video was an introduction, th-cam.com/video/BY_ZjPGqJJk/w-d-xo.html, but I plan to make enhancements as videos, and some of them will include API designs.
One of those enhancements I am working on is a utility ModQL (for Model Query Language), which I might make a "rebuild from scratch" video if I find a way to make it not too overwhelming. However, this would be a great lib tutorial as it uses a lot of patterns, including procedural drive macros and Serde deserialization implementation.
For now, ModQL (crates.io/crates/modql) implementation is still experimental, but the concept/design (.e.g., joql - joql.org/) is something we use in our production applications, so it is relatively mature and value-tested.
The challenge is how much context and information I can put in a video before it becomes overwhelming and confusing. In my previous video, I found I could go up to 500 to 1k loc without getting too confusing. The catch is that ModQL might need a lot of contexts, as it follows the JSON-RPC model over REST, and could be seen as an alternative to GraphQL, and mistaken for an ORM.
Now, I am all ears if you have a more straightforward API suggestion.
Great video! Which tools did you use? I am thinking of creating a couple of videos myself.
Davinci Resolve.
@@JeremyChone thanks a lot!
Basic question but how can you have a function return type as: Result
do you not require 2 arguments like Result?
Good question.
It is because the Result type is aliased in the prelude.rs.
This is a common pattern in Rust. It allows normalizing all of the Results of your crate.
So, in the prelude.rs, I have
// re-export the crate Error
pub use crate::error::Error;
// alias Result to be the crate Result
pub type Result = core::result::Result;
And because I typically import the prelude
use crate::prelude::*
Then, I can just use this Result type alias.
Here the video about the code layout I use : th-cam.com/video/oxx7MmN4Ib0/w-d-xo.html
@@JeremyChone I really appreciate you taking the time to answer. I find your videos incredibly helpful so thank you so much
Where is the ten upvote button... This is very eye opening.
❤❤❤
let else doesn't work for me and I updated before but it says le else is unstable and I'm stuck can't find how to write in a match close
What do you get if you do: rustc -V
I get: rustc 1.65.0 (897e37553 2022-11-02)
@@JeremyChone yeah you are right I'm on 1.64 my bad
Implicit clone seems to be bad API performance-wise. Consuming pattern seems to be more rusty.
Agree,
1) non consuming pattern .build(&self) is ok, but mostly when the builder data are not directly used for building the object (so, no implicit clone, because no clone is needed).
2) non consuming pattern with .build(&mut self) is bad IMO. I do not see why that would be a good idea.
3) The consuming pattern, with build(self) is the one most aligned with Rust IMO, and my default go to when I need a builder pattern.
That depends. Does the clone happen only once or is it called several times? If there's only one clone when doing thousands or millions of operations, it is okay. But if clone happens several times during the runtime, then it might slow the CPU by a lot. Creating and copying memory is extremely slow in comparison to just CPU calculations.
Hope this won't come off as rude but, have you considered doing subtitles for your content? It's probably because I am just a beginner so I'm not familiar with all of the lingo (and also don't have any friends who speak with your specific accent) , but there are times when I just cannot understand a word or sentence of your narration.
I think TH-cam does the auto CC, and it is not too bad. Try to press the CC icon somewhere. Sometime I have to correct couple of words, but overall it should be ok.
@@JeremyChone I am willing to contribute to adding subtitles to your videos
Actually, as a non-native, I find it very easy to understand.
Define method from enum, it is better than String.
Ho yes, I think there was a comment for this on the code. I just simplified this part for the example.
Voudrais tu faire un cours de rust en français ? Le "voilà" ta démasqué
One day I might, but I am in a catch-22 situation. I still have my airport accent when I speak English, but it's tough for me to talk tech in French (I have been living in San Francisco most of my tech life).
I will give it some thought, though. My planned 2023 Full Rust Course could be a good candidate for a VF.
The build pattern is one of the Achilles' heel of Rust because of the borrow checker. The "-> &mut self" denies the fluent method and the "-> Self" denies later addition before doing the build without reassigning, so there's no escaping boilerplate code. I would definitely avoid to copy all the fields in the builder though, that's unnecessary. Instead, only add one field with the target object (Request) which is preset to default, it will simplify a lot. You can hand it over in the build() method without overhead.
You can literally have a macro to generate the boilerplate for you
@@diadetediotedio6918 What do you mean by "literally"? And for which part? Generally speaking, I don't think macros are the solution to every shortcoming, you would just have macro boilerplate code instead, that you would need to include in each crate. I'd rather fancy a way to tell the compiler that when an object is not actually moved in a function that takes ownership, it can be considered as still owned by the original owner. Or maybe that's something the compiler will be able to work out on its own one day. In this case, the setters could benefit from it, but I've seen this annoying pattern in other situations.
I would differ. I think the Rust data model suits the builder pattern very well, especially for the consuming builder pattern.
The Rust mutable model makes them thread-safe and efficient while making cloning explicit. The builder re-assignment is a minor cosmetic oddity but a totally logical one.
Now, I would agree that the Builder Pattern should not be abused. It should be an exception for those "structures" that would gain to be built in steps or are relatively complex. Good examples of well-thought-out are Bevy, Tauri plugins, Clap command tree, and many Web framework routing/service structures. SQL Builders are an excellent fit for this pattern as well.
I used to avoid builder patterns in Java, but now, in Rust, when appropriate, I find them very productive.
The TypeState builder pattern can also be powerful and relatively natural in Rust (I remember in my Java days having issues trying to chain methods that return variation of the self type). TypeState Builder Pattern video coming soon...
The re-assignment and the other ways to work around the borrow checker make the code noisier, but you're right, it's not a blocking issue. Hopefully it will get smarter with time when ownership transfer in functions can be detected by the way they are used.
I think build patterns are hard to avoid especially in Rust because there are not many alternatives if you want to avoid mutable fields. Another good pattern is optional named arguments: in languages like Kotlin or Python for example, they are a huge benefit in that regard. That's another feature Rust would greatly benefit from.
Subtitles please 🥺
Does the CC does not show for you? YoutTube auto caption is quite impressive with accents. I reviewed it, and seems to be ok.
Let me know if the CC does not work for you.
@@JeremyChone Yes it works fine, thanks!
@@JeremyChone At 2:32, you said "trait", but the CC said "trade". That's minor.
Slightly more difficult is that the CC text obscures the bottom of the video. The alpha value for the CC text is a little less than 1.0, which makes it a little harder to read when there is other text underneath. If only TH-cam would make the content taller so that the CC text appears beneath the video...
I ended up turning CC off, and then using the keyboard shortcut to jump back 10 seconds if I needed to hear something repeated. Slowing playback speed to 75% is sometimes helpful.
I think what you're doing is fine. I have several alternatives to help me if I'm having a hard time understanding.
@@markday3145 First, thanks for the "trade" CC miss. I missed it in my quick review. I just fixed it.
Second, thanks for the detailed and constructive feedback.
I will see what I can do for the CC location, but it is hard to factor this in and have an optimum code layout. But I get your point.
The accent and mispronunciation are bugs I have tried to fix for a while, but it is hard to fix something you can't see (i.e., hear). I will work on enunciating better (it might take some time).
Now the good news is that it has been said that people that listen to me speak English end up understanding French better. So, time to plan a Paris vacation!
@@JeremyChone I assume you're a non-native English speaker. In that case, I think your English is actually pretty good. Your accent is... you; and it's not that hard for me, either.
I've got tinnitus and sensory processing disorder. Both make it more challenging for me understand human speech in general, and especially in videos.
Dialogue in videos is harder if I'm trying to concentrate on both the visual and the audio at the same time. Not speaking while the visuals are changing helps, but I'm not asking you to try to accommodate that. Being able to jump back 10 or 30 seconds, or to slow down playback, both go a long way to making it easier for me.