Can you do an episode of Crust of Rust with the topic of error handling. You could explain how to manage different error/result types from multiple crates and how to bundle them up. If I'm not mistaken there are some crates like Failure or Fehler which help with error handling.
Especially things like: - Dealing with a peekable iterator over a Result whose Err() does not implement Clone - so you can't use ? because you have &Result but can't move the Error either. - Dealing with Results whose Err() does not implement Try - so you cannot use ? (is the only option to match and convert the error?) - Dealing with Results whose Err() does not implement std::Error::Error - Working with all of the above with anyhow and thiserror
@@BertPdeboy Can confirm. If you look at my first several rust projects, none of them handle errors the same way. I think I'm in a better place now, but uh.. how does one know without peer review?
Actually would be super helpful, recently got into a corner when I was trying to handle errors from two different types of objects ? main function or that forced me into using .unwrap() for both cases. This is obviously not ideal, because segfaults are not fun for the CPU and will crash the program. The documentation on this, is actually not that helpful. doc.rust-lang.org/stable/rust-by-example/error/option_unwrap.html as really none of that means anything to me.
For anyone watching this more recently: there's a nightly feature called macro_metavar_expr that gives you special expressions to count the number of repetitions of a metavariable, among other things. So you won't have to write that @COUNT helper macro thing.
Thank you, Jon, for a very useful stream. Suggestion for another Crust of Rust: how to use all these Rc, Cell, RefCell, Weak, etc. After reading all the documentation I've managed to compile my code, but it ended up panicking all the time. So a good explanation about shared ownership could be useful for many, I think.
1:10:17 - it has to be in that order because it needs to hit the non-recursive case first. Technically [ ] satisfies both conditions so you want it to satisfy the the one that does work rather than the one that recurses back to itself.
Awesome video. And can I just say that responding to comments on chat is very helpful. I did not watch the live stream but your viewers asked all of the questions I thought of during the session.
Great video. :) Sorry for being incredibly picky again ;-), but: At 1:25:38 you misread the docs (hence the confusion about it not being const) - you **can** use it in Rust 1.2 - it just warns you that in that version it is not a const expr (although still might be evaluated by the compiler in an optimized build), in much more modern Rust, as you showed, it is const. It seems to me the book is quite out-dated since last commits to it were 4 years ago.
And it warns you about it, as the trick expanding to `0 + 1 + 1 + 1 + 1…`, although much heavier for the compiler, did produce a constant expr already in 1.2 (as it was just simple arithmetic on constant integers). So in Rust 1.2, if you absolutely needed const expr to be produced, then the `+ 1` trick might have been preferred. Today it seems it doesn’t make sense to use it anymore, and the slice length of an array of units is always the ‘correct’ way.
The demonstration with const was really useful. By the way, what are your vim settings? (and terminal emulator if you are running it directly in the terminal?) - I have a few problems with rust-analyzer and auto-completion plus showing the types in gvim.
He has a separate video on his setup called "Desktop and editor setup for Rust development". His vim configs are available on his github. I think he is using Neovim.
Macros in lisp are way easier to implement, because the language is represented as a recursive list. However the parentheses get old really fast, and I don't usually write enough macros to make up for it. I prefer the rust way of creating macros, because the language can be way more expressive than lisp in general.
I used to have relative line numbers, but I realised I hardly ever looked at them, they only made it harder to communicate with others when screen sharing.
As soon as he showed the counting with empty I was like... wait, we can just substitute with whatever this way? So like... with a 1? like I could turn each element into an expanding (1+) pattern and terminate with a 0?...Paused and tested. Quite pleased. Assuming this is what we do next... nope... I guess it makes sense that there is a depth limit for ast. Wouldn't have thought a mere 500 levels would be it though. Guess there isn't really a good reason to have more than 512 levels deep ast with normal code. Could add a pipeline in the tokenizer stream to convert literal a + literal b pattern and to a simplified literal sum, which would be streamable. But then you loose the mapping capabilities an ast provides for those tokens. Makes sense that they can't do that in the standard toolchain, ast is the sacred source all IDE's, formatters, linters, checkers, and transformers depend on.
Could anyone explain what happens at 1:20:49? Why do we need to explicitly mention the type? Are there different implementations for len for each type? And if that is the case why? I thought len just returns the number of elements in an array/slice/Vec?
how does he do the thingy in vim where he highlights the code and then pastes it below without moving the curser, he really needs the thing that shows his keyboard keys
Can someone help me? Does std::iter::repeat().take() implement TrustedLen which provides a size for "extend" to get the right mem allocation ahead of time? Im new to Rust and I read the source code to build this intuition, so a confirmation could be of great help!
I believe it does. From memory Take implements it if the Iterator it wraps implements it, which I believe it does. If I remember right, the memory allocation will take advantage of the Iterator size hint regardless, which take definitely gives.
Procedural macros are a much thornier beast, and probably wouldn't fit into this format. I've done a longer stream on procedural macros though, so maybe you want to give that a watch? ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-geovSK3wMB8.html
And the "awesomeness" continues !! Thank you so much for making these videos ! In addition to the super-human intelligence (MIT and that too PDOS !), you have an exceptional ability to demonstrate things by building upon concepts in a very approachable way. Thank you!
I'm new to Rust ,how to do something like this in rust e.g. if I have a byte/char buffer : char ptr[] = { 0xAA, 0xAA,0xBB, 0xBB, 0xDD, 0xDD, 0xEE, 0xEE }; int a = *(int*)&ptr; could someone write this C (casting) code in Rust? so that a == 0xbbbbaaaa? thanks
That style of type punning is illegal even in C (strict aliasing rule) and you should memcpy from char array to int. In Rust you have several methods associated with integer types (from_ne_bytes/from_le_bytes/from_be_bytes) that let you do that e.g. let buf: [u8; 8] = [0xaa, 0xaa, 0xbb, 0xbb, 0xdd, 0xdd, 0xee, 0xee]; let value = i32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]);
hey, is it possible to have a macro that only receive item that implemented some trait? to be more specific im trying to implement my own HashMap and a macro that receives an RandomState in order to use it in HashMap::with_hasher. thanks in advance!
No, you can't add trait bounds to macros. However, most of the time there isn't really a need to. If someone uses a type that doesn't work in the generated code, the compiler will simply give them an error in the generated code instead :)
@@jonhoo thank you! I thought of that too after seeing it in you video :) But now I have a problem because I have 2 macros that receive one element (one for count one for RandomeState) and which ever I declare first the compiler indicates an err on the other because they both could fit the pattern.
thank you for the nice video - just a remark where you might want to re-evaluate the expression multiple times - if the expression produces different values every time evaluated. An example for this would be a random number generator, or measuring something from a device. Arguably this could also be done by providing an iterator (I guess - but I'm only a C++ guy), but at least traditionally random number generators are simple functions that produce different numbers when evaluated multiple times.
Ah, but even then it's not clear that you want the expression to be evaluated multiple times. Maybe you're using the random number as an index in multiple correlated data structures, and the _same_ random number therefore has to be used for each. But you're right that there _are_ cases where you want to execute something multiple times too. Generally, there isn't a good way to indicate whether your macro evaluates multiple times or just one currently in macro syntax, except by documenting it well.
@@jonhoo sure, the default should be not to evaluate multiple times - and only create special versions for exactly the use-cases (with good names so that nobody calls them by accident)
Not sure this is performant, however you could just have an expression that evaluates to 1 and add it for the counting? ``` macro_rules! avec { ($($el: expr),*) => {{ let len = 0usize $(+ {avec!(@SUBST; $el); 1})*; #[allow(unused_mut)] let mut v = Vec::with_capacity(len); $(v.push($el);)* v }}; (@SUBST; $_el: expr) => { () }; } ``` So this would reduce to `0 + {(); 1 } + {(); 1 } + {(); 1 } + {(); 1 }`, or `0 + 1 + 1 + 1 + 1 + 1 + 1`. What do you think?