5 ms·
As a C developer for embedded, considering Rust for a long time now, the panic thing is something that bothers me. I don't want/I can't panic. I want to be retu
by scoutt 6y ago
As a C developer for embedded, considering Rust for a long time now, the panic thing is something that bothers me. I don't want/I can't panic. I want to be returned false, null or whatever.
Do I have to check if an external crate that I am using would panic? If so, how do I prevent the crate from panicking?
From my perspective, Rust is kind-of designed to support code like this (non real Rust code follows):
my_struct.do_something()
.get_this()
.get_that()
.as_ref()
.as_paper_airplane()
.unwrap()
What if some of those calls fail? How do I detect an error? Alright, it might panic, but what if I have to keep going forward, even in case of an error, in my embedded application? Should I split the different function calls and check for errors?
Kernel development intersects with embedded development in many points. I'm sure I am not the only one with these doubts.
- whb07 6y agoIt would really panic if it ran into an unhandled condition that you explicitly sent it to or left as is. What I mean is pretend you have some external reading/data coming in and you parse it to have the field "name" to always have a value of "scoutt". match data.name { "scoutt" => return True, _ => panic!(), } In that case, because you "know" that the name field cannot be anything but your specific value ,you don't really mind the matching pattern and Rust is satisfied. But then you get a runtime panic! because all of the sudden the value changed to "joe". Long story short is as long as you handle and declare ahead of time the proper fail conditions and paths to take at compile, you really shouldn't come across a panic.
- loeg 6y agoCalls that fail return `Result<OkType, ErrorType>`. Here's something like what `unwrap()` does (actual implementation may have some nuances I didn't capture here): impl<OkType, ErrorType> Result<OkType, ErrorType> { fn unwrap(self) -> OkType { if Ok(x) = self { return x; } panic!("not ok"); } } So, if you don't accept panic, don't call `unwrap()` unless you're very sure that the returned value couldn't fail in the way you're using the API. You could think of `unwrap()` in Rust as semantically similar to `assert(error == 0)` in C. Avoid or use in the same places you would avoid or use that assertion in C. That means you must handle the Err case, often by just returning it to the caller (like C). The ? operator is syntax sugar for this: return if err, or give me the ok value if non-err. fn my_fn() -> Result<(), Error> { // if this failed, returns some Err() let my_x = my_struct.do_something_that_can_fail()?; // ditto let my_y = my_x.something_else_that_can_fail()?; // no failures? Ok Ok(my_y) } Of course, these can be chained with the exact same characteristics: fn my_fn() -> Result<(), Error> { // Equivalent Ok(my_struct.do_something_that_can_fail()? .something_else_that_can_fail()?) }
- scoutt 6y agoThank you! Yes, the last 2 examples are much clearer to my eyes. And it's on the tutorial too: File::open("hello.txt")?.read_to_string(&mut s)?; I should pay more attention :) But I also deduce 2 things: - That unwrap should not be used in the kernel, correct? - That I should be very careful about the external libraries I use and look for the presence of panic! and/or unwrap, or use https://docs.rs/no-panic/0.1.13/no_panic/ https://docs.rs/no-panic/0.1.13/no_panic/ as jononor commented here below. But I guess this might rule-out some useful libraries from a project.
- gpm 6y ago> - That unwrap should not be used in the kernel, correct? Yes. I guess the exception is if it will only panic on a kernel bug and you're ok with that (that's what the bug macro is for, no)? E.g. if I have `let x = Some(2); let y = x.unwrap();`. The second statement panics if x is None, but that would only happen in the event of a bug, so you might be ok with it. This does happen sometimes in real code, `if some_list.len() > 2 { let x = some_list.pop().unwrap() }` will never panic, because pop() only returns None (think null) if the list is empty, but we just checked it has at least two elements. I'm not sure what the kernels stance on this sort of code will be. > - That I should be very careful about the external libraries I use and look for the presence of panic! and/or unwrap, In the context of the kernel, I imagine you just don't use external libraries at all. In a more general context, yes, you need to understand when any libraries you use might panic. The general convention that is honestly not very well followed is to document panics that result from usage errors, and to allow for panics caused by library bugs.
- loeg 6y agoNo problem! > That unwrap should not be used in the kernel, correct? More or less. There could be situations where you know the error is impossible and it is reasonable to unwrap. But it's a good rule of thumb. > That I should be very careful about the external libraries I use and look for the presence of panic! and/or unwrap, or use https://docs.rs/no-panic/0.1.13/no_panic/ https://docs.rs/no-panic/0.1.13/no_panic/ as jononor commented here below. But I guess this might rule-out some useful libraries from a project. Absolutely! Use of unsafe is another potential watch-point.
- jononor 6y agoMaybe something like this? https://docs.rs/no-panic/0.1.13/no_panic/ https://docs.rs/no-panic/0.1.13/no_panic/
- davidhyde 6y agoIf do_something(), get_this(), get_that() and as_paper_airplane() all returned a Result then you could write your code as follows: my_struct.do_something()? .get_this()? .get_that()? .as_ref() .as_paper_airplane()?; If the error type of the result differs you can use map_err or write a converter by implementing the From trait for your the custom error struct you want to map to. The one thing you can't find out is if the calling function will panic in its function or a function that it calls. When you write embedded libraries you are never supposed to panic but there is nothing to enforce this which is, admittedly, not ideal.
- iknowstuff 6y agohttps://doc.rust-lang.org/std/result/ https://doc.rust-lang.org/std/result/