Mutant Bob
Mutant Bob

Reputation: 3549

What is the difference between these two Rust error handling blocks?

I had some code

fn read_inner<'b>(&'b mut self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + 'b>> {
    ...
    buf[rval] = reader.next_spi_byte()?
...

that worked in an std environment. I used the core_error crate to make the transition to no_std a little easier. But now I'm getting errors.

error[E0277]: `?` couldn't convert the error to `Box<dyn core_error::Error>`
  --> /home/thoth/src/rust-esp32-experiments/http-camera/webcam-applib/src/camera.rs:90:47
   |
90 |             buf[rval] = reader.next_spi_byte()?;
   |                                               ^ the trait `From<CamError<<CS as embedded_hal::digital::v2::OutputPin>::Error, ES, EI>>` is not implemented for `Box<dyn core_error::Error>`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following other types implement trait `From<T>`:
             <Box<CStr> as From<&CStr>>
             <Box<CStr> as From<CString>>
             <Box<CStr> as From<Cow<'_, CStr>>>
             <Box<T> as From<T>>
             <Box<[T], A> as From<Vec<T, A>>>
             <Box<[T]> as From<&[T]>>
             <Box<[T]> as From<Cow<'_, [T]>>>
             <Box<[T]> as From<[T; N]>>
           and 4 others
   = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, CamError<<CS as embedded_hal::digital::v2::OutputPin>::Error, ES, EI>>>` for `Result<usize, Box<dyn core_error::Error>>`

I tried to work around it a couple of different ways

buf[rval] = reader.next_spi_byte().map_err(|e| Box::new(e))?;
buf[rval] = reader.next_spi_byte().map_err(|e| Box::<dyn Error+'b>::new(e))?;
buf[rval] = reader.next_spi_byte().map_err(|e| {
    let tmp: Box<dyn Error + 'b> = Box::new(e);
    tmp
})?;

Only the last one actually worked. The other two triggered errors

error[E0277]: `?` couldn't convert the error to `Box<dyn core_error::Error>`
  --> /home/thoth/src/rust-esp32-experiments/http-camera/webcam-applib/src/camera.rs:91:72
   |
91 |             buf[rval] = reader.next_spi_byte().map_err(|e| Box::new(e))?;
   |                                                                        ^ the trait `From<Box<CamError<<CS as embedded_hal::digital::v2::OutputPin>::Error, ES, EI>>>` is not implemented for `Box<dyn core_error::Error>`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following other types implement trait `From<T>`:
             <Box<CStr> as From<&CStr>>
             <Box<CStr> as From<CString>>
             <Box<CStr> as From<Cow<'_, CStr>>>
             <Box<T> as From<T>>
             <Box<[T], A> as From<Vec<T, A>>>
             <Box<[T]> as From<&[T]>>
             <Box<[T]> as From<Cow<'_, [T]>>>
             <Box<[T]> as From<[T; N]>>
           and 4 others
   = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<CamError<<CS as embedded_hal::digital::v2::OutputPin>::Error, ES, EI>>>>` for `Result<usize, Box<dyn core_error::Error>>`

error[E0599]: the function or associated item `new` exists for struct `Box<(dyn core_error::Error + 'b)>`, but its trait bounds were not satisfied
  --> /home/thoth/src/rust-esp32-experiments/http-camera/webcam-applib/src/camera.rs:92:81
   |
92 |             buf[rval] = reader.next_spi_byte().map_err(|e| Box::<dyn Error+'b>::new(e))?;
   |                                                                                 ^^^ function or associated item cannot be called on `Box<(dyn core_error::Error + 'b)>` due to unsatisfied trait bounds
   |
  ::: /home/thoth/.cargo/registry/src/github.com-1ecc6299db9ec823/core-error-0.0.0/src/error_trait.rs:19:1
   |
19 | pub trait Error: Debug + Display {
   | -------------------------------- doesn't satisfy `dyn core_error::Error: Sized`
   |
   = note: the following trait bounds were not satisfied:
           `dyn core_error::Error: Sized`

Why is the second one different from the last/third one?

The suggestion from isaactfa

buf[rval] = reader.next_spi_byte().map_err(|e| Box::new(e) as Box::<dyn Error+'b>)?;

appears to work. It still puzzles me that Box::<dyn Error+'b>::new(e) can not be converted to a Box<dyn Error+'b> by the ? operator.

Upvotes: 0

Views: 92

Answers (1)

kmdreko
kmdreko

Reputation: 60272

Essentially this boils down to how ? works and how unsized coercions work.

Going from T to dyn Trait (if T implements Trait) requires an unsized coercion, as does going from Box<T> to Box<dyn Trait>. The ? operator does not do unsized coercions, it uses the Into conversion trait. It may have worked before with the std::error::Error trait since there is an Into implementation that does the unsized coercion internally E: Error to Box<dyn Error>. It does not appear that core_error::Error has such an implementation, so that's why the first method doesn't work.

The reason Box::<dyn Error+'b>::new doesn't work is because you cannot have a variable of type dyn Trait. A dyn Trait is a dynamically-sized type and must be used through some kind of indirection: a reference &, a Box, etc. Basically you are trying to coerce the type into a trait object too early; new would have to accept a dyn Error parameter, which is not allowed.

The way the last method works is because you put the value in a Box<T> and then coerce it to a Box<dyn Trait>. Then ? will work since it will just use the identity blanket implementation of Into.

Upvotes: 1

Related Questions