An aside: what’s the template that gets articles formatted this way in GitHub pages? I found it very appealing.
A guide to error handling in Rust
51–60 of 76 posts
Re: A guide to error handling in Rust
#52I feel like this document makes the Try operator (?) and its associated trait more mysterious than necessary. Most people probably won't need to implement Try, especially before it is stabilised, but it's not that much more complicated than say, AddAssign the trait which you implement to make the Add Assignment (+=) operator work on your type. The key trick of Try is that it converts something (by default an Option o…
I do some hobby projects in Rust. One gotcha that I hit was using ? in sample code in documentation. It didn't work, so I had to replace all of my ? with .unwrap(). I generally consider .unwrap() a poor example, because it encourages writing code that could crash a program unnecessarily.
Re: A guide to error handling in Rust
#53Earlier quoted context omitted.
> It makes total sense for one to exist. I disagree: IME, when you add a float and an integer, you want to cast float to integer 50% of the time, and integer to float the remaining 50% of the time. Even if it leads to more verbosity, I prefer arithmetic operations to be endomorphisms and use explicit casts.
I'm sure you can just use a linter or configure the compiler to error out in such a case if there are really no use-cases, no?
let x=19/10+0.5
I want to decide for myself if my result is 2.4, 1.5, 1, or 2 in the example aboveRe: A guide to error handling in Rust
#54Earlier quoted context omitted.
I'm sure you can just use a linter or configure the compiler to error out in such a case if there are really no use-cases, no?
What do you mean? It already errors out if you try adding them together because it requires explicit casts. What the comment you're replying to is saying is that it's better to just explicitly cast than figure out what the compiler guesses. let x=19/10+0.5 I want to decide for myself if my result is 2.4, 1.5, 1, or 2 in the example above
Re: A guide to error handling in Rust
#55Earlier quoted context omitted.
Well, that depends. If the union consists of two types that share the same underlying structure, then obviously at runtime we can never know what the value is. But otherwise we can. And this is something that we will know at compile-time, so we can prevent runtime-checks that would not work.
I must not be understanding what you're asking for because it sounds like anonymous sum types.
But essentially, when it comes to union types, they behave like sets. The compiler merges them. (A | B) | (A | B) is the same as A | B. But for sum types (even anonymous ones such as tuples) the compiler can't merge them because that would lose information (if the result is from the first A | B or the second one). Instead, you end up with a nested structure.
Which one is desired depends on the use-case, but it's definitely different.
Re: A guide to error handling in Rust
#56Earlier quoted context omitted.
For anyone interested in what this would look like in Rust now, there's two ways. For libraries, people tend to recommend the thiserror crate. Code sample[0]: #[derive(thiserror::Error, Debug)] enum Error { #[error("One")] One(#[from] Error1), #[error("Two")] Two(#[from] Error2), } fn foo(r1: Result , r2: Result ) -> Result { let i1 = r1?; let i2 = r2?; // ... } Whereas for binaries, people usually recommend anyhow.…
For simple tasks you can get away without any external crates by using `Result >`. But it's much more comfortable to use thiserror or anyhow in the long run. fn read_string() -> std::io::Result { Ok("123".to_owned()) } fn main() -> Result > { let s = read_string()?; // io::Error let n = i32::from_str_radix(&s, 10)?; // num::ParseIntError println!("read number: {n}"); Ok(()) }
Re: A guide to error handling in Rust
#57Too bad Rust doesn't have union types (aka adhoc / anonymous unions) yet. Without them, using typed errors is very clumsy. Optimally, you would write the following code: fn foo(r1: Result , r: Result ) { let i1 = r1?; let i2 = r2?; // ... } and Rust would infer the return type to be Result without having to do any extra definitions or conversions.
For anyone interested in what this would look like in Rust now, there's two ways. For libraries, people tend to recommend the thiserror crate. Code sample[0]: #[derive(thiserror::Error, Debug)] enum Error { #[error("One")] One(#[from] Error1), #[error("Two")] Two(#[from] Error2), } fn foo(r1: Result , r2: Result ) -> Result { let i1 = r1?; let i2 = r2?; // ... } Whereas for binaries, people usually recommend anyhow.…
Re: A guide to error handling in Rust
#58Earlier quoted context omitted.
For anyone interested in what this would look like in Rust now, there's two ways. For libraries, people tend to recommend the thiserror crate. Code sample[0]: #[derive(thiserror::Error, Debug)] enum Error { #[error("One")] One(#[from] Error1), #[error("Two")] Two(#[from] Error2), } fn foo(r1: Result , r2: Result ) -> Result { let i1 = r1?; let i2 = r2?; // ... } Whereas for binaries, people usually recommend anyhow.…
what is the TL;DR difference between anyhow and Box ?
Re: A guide to error handling in Rust
#59Too bad Rust doesn't have union types (aka adhoc / anonymous unions) yet. Without them, using typed errors is very clumsy. Optimally, you would write the following code: fn foo(r1: Result , r: Result ) { let i1 = r1?; let i2 = r2?; // ... } and Rust would infer the return type to be Result without having to do any extra definitions or conversions.
Even if the "Ad hoc union" becomes a thing in Rust, you are not likely to get inference of return types. The return type is part of the function signature and Rust deliberately doesn't infer signatures, in languages with "too much" inference it's impractical for the human programmer to keep track of types because it's all inferred, this has started to be a problem in C++ as more and more things are auto. Rust has som…
Re: A guide to error handling in Rust
#60Earlier quoted context omitted.
I know that this is a common wish, but anonymous sum types have pretty catastrophic impacts on type checking and lead to all sorts of bizarre corner cases like the following: let a = if cond { 1 } else { 1.0 }; a + 3 Now, the error would be pushed to the `+` operator because there isn't an `Add` for `f32 | u32`. Granted, this is a trivial example, and a programmer can easily see through it, but in general this can ge…
> Now, the error would be pushed to the `+` operator because there isn't an `Add` for `f32 | u32`. Why not? It makes total sense for one to exist. This is something the language needs to deal with, not the programmer. But the programmer always sprinkle annotations so that errors can only reach so far.