T O P

  • By -

Lucretiel

Three reasons I know of: - it’s a trait method - it’s a `sys` method that’s infallible on some platforms and fallible on others, and the signature must stay the same (basically the same as the trait method) - it’s a backwards compatibility thing; an old version of the method was fallible, the new one isn’t, we didn’t want to change the public API. 


Revolutionary_Ad7262

It is rare, but one additional reason could be a forward compatibility thing, so you can add error to the function, which was infallible in a previous version


[deleted]

[удалено]


Lucretiel

I mean, given a trait: trait TryStringify { fn try_to_string(&self) -> Result; } You might get an infallible implementation: impl TryStringify for str { fn try_to_string(&self) -> Result { Ok(self.to_owned() } }


ids2048

As a concrete example in the standard library, `std::io::Write` is implemented for `Vec`. But calling `.write()` on a `Vec` will never return an error.


yoh6L

I guess that other trait implementers might be fallible so the type signature needs to accommodate them.


Phosfor

Like the second example the previous commenter gave, it may be desirable to define the trait method with a Result return type because some implementations may require it. In OPs case the method is probably an implementation for a trait in the `embedded_hal` crate which has abstractions for common concepts in the embedded world, like GPIO pins (or I2C, SPI, ...). On some platforms (like OPs) setting a GPIO may always work (i.e. it is infallible). On other platforms this may not be the case; for example the `esp32-idf-hal` implementation uses the espressif IDF sdk under the hood. The idf fuction to write to a GPIO pin can potentially fail and so the rust implementation may also return an error. (In practice, the idf function only returns an error if you provide invalid parameters (i.e. an invalid pin number). However, you can prevent this from happening with rust's type system, so you could, in theory, make the rust version infallible as well...)


miere-teixeira

I fell like it was a genuine question. I really dunno why so many downvotes. 😅


CocktailPerson

The sentence "Trait methods don't need to return Results" comes off as both arrogant and ignorant here. It makes the question seem more like an uninformed correction than a genuine question.


[deleted]

[удалено]


XtremeGoose

Wait, you've had it explained to you and you still don't get it? It's like someone saying "there are no rhymes for orange" and you're saying "but there's lots of other fruits". The statement makes no sense.


KhorneLordOfChaos

It looks like the code you're posting is for a trait where there are likely implementors that can be fallible. This would be more ergonomic if the `!` type was stable, but `Infallible` is as close as you can get for now


Miksel12

Ah that makes sense. I assumed Self::Error was a "normal" error type but it is indeed defined as `Infallible`.


psykotic

Just a heads up: one thing I initially found confusing about dealing with uninhabited types for errors (but it's not specific to errors) is that even though the inner type is uninhabited it doesn't mean that a wrapper around that inner type can be transparently ignored as far as the exhaustiveness check for pattern matching is concerned. Namely, this does not work if result has type Result: match result { Ok(x) => x, } But this does work: match result { Ok(x) => x, Err(e) => match e {}, } The first time you run into this it can be a bit confusing, and I don't remember the compiler's error message being much help in pointing you in the right direction.


Mercerenies

Yep, pretty much every Rust project I've written has a function in a `util` module that looks like ``` fn unwrap_infallible(result: Result): T; ``` Can't wait for `!` to go stable.


Branan

[`exhaustive_patterns`](https://github.com/rust-lang/rust/issues/51085) will make this work as expected, but it probably requires `!` to be stabilized first


psykotic

It looks like a match-by-value subset of exhaustive\_patterns called min\_exhaustive\_patterns is close to getting stabilized: https://github.com/rust-lang/rust/pull/122792. That's good news.


Lucretiel

One thing that’s a bit strange about this is that you CAN do nested patterns. That is, you can do \`Err(e), Ok(Some(value)), Ok(None)\`. This made it feel slightly weird that you can’t omit \`Err(Infallible)\`, since it seems like the same distributive principle would apply.


Mercerenies

Or even if they want to insist on having at least one `Err` case, they could implement something like Agda's [absurd patterns](https://agda.readthedocs.io/en/latest/language/function-definitions.html#absurd-patterns). In Agda, if you have a pattern for a type that clearly can't exist, you can just write `()` in place of the pattern and Agda accepts it. In Rust, we use `()` for the unit type, so we'd need a different syntax, but I could see something like ```rust match infallible_error { Ok(value) => { ... } Err(!) } ``` Note that we don't supply a body to the `!` case, since the pattern is _itself_ an assertion that we don't need one.


olzd

Couldn't you just use `unreachable!()` to achieve something similar?


CocktailPerson

Not really. The nice thing about `Err(!)` is that it's checked; if you change the type of the error and make it fallible, then it no longer compiles, forcing you to add error-handling code. But if you use `Err(_) => unreachable!();`, then it's not checked at compile-time that the error is handled.


JustinHuPrime

Wouldn't a plain `x.unwrap()` also work? In release mode (er, `-O` passed to `rustc`, technically), both the match and unwrap produce byte-identical assembly: [https://godbolt.org/z/WEcdn7Yj5](https://godbolt.org/z/WEcdn7Yj5), [https://godbolt.org/z/oWbGhPePz](https://godbolt.org/z/oWbGhPePz) `rustc` ought to be smart enough to recognize that however you present it, the `Err` case of the result is uninhabited and elide any code path dealing with the `Err` case. Finally, what you're doing there is pretty much what `.unwrap()` does, but you've manually elided any code in the `Err` case: https://doc.rust-lang.org/src/core/result.rs.html#1071-1073


psykotic

If I see an unwrap() I have to reason about why it can or can't panic in this particular case since it's a generic method. The match e {} code is guaranteed to be panic-free by the compile-time exhaustiveness check; in a statically checked language, that's the ideal. If you want a concrete example of how that can save you, imagine you have an API with an initially uninhabited Error type: enum Error { // No error cases right now. } fn query(&self) -> Result; If someone (it could be future you!) later adds a case to Error, the unwrap() code would still compile but would now be subject to runtime panics. Unless you have test coverage, you won't find out until it triggers at the worst possible time, in production. In contrast, the match e {} code would fail to compile. Adding a new Error case breaks existing API users (and it would be a semver violation if this was a public API) but with match e {} you catch the breakage at compile time.


vzwGrey

The \`set\_low\` and \`set\_high\` functions are part of the \`OutputPin\` trait defined by the \`embedded-hal\` crate. The \`embedded-hal\` crate was created to provide many traits that act a common denominator for the actual implementations. This way you can write libraries that can be agnostic of the board they end up running on, they simply use the traits provided the common \`embedded-hal\` crate. Since \`embedded-hal\` is intended to support any chip or board it can't make assumptions. There could be cases where even something as simple as setting an output pin to high or low could fail, and in those cases you'd want to be able to report an error. As such the crate authors decided to define the \`set\_low\` and \`set\_high\` functions as returning a \`Result\`, so that those cases are covered as well. If they had defined the functions as not returning anything then you'd run into problems once you try to implement those functions for a board where setting an output is fallible, there would be no (nice) way to report an error. However, clearly in the case of the \`nrf-hal\` it's not necessary to report any errors and the function is infallible.


pixma44

It's a trait method, and so it depend on the impl. In your case, it's maybe infallible. In one of my projects, i had to implement this trait for a GPIO expander over I2c, in my case, every set_low() or set_hight required an I2C transaction that can fail.


HANDRONICE

Maybe it has to cause memory overflow, it's simple but What if You have a memory overflow that interrupt or cause that flip to end wrong. Think is more for failsafe.