Fred Hors
Fred Hors

Reputation: 4136

A have a function that doesn’t compile when within an Option<> type but that does compile on its own outside an Option<> type. Why?

I have this function:

pub async fn player(&self, id: &str) -> Result<Option<Box<dyn AsyncRead + Send>>> {
    // use id here...
    let file = tokio::fs::File::open("player.pdf").await?;
    let res = Some(Box::new(file));
    Ok(res)
}

It doesn't work:

error[E0308]: mismatched types
    |
46  |         Ok(res)
    |         -- ^^^ expected trait object `dyn tokio::io::AsyncRead`, found struct `tokio::fs::File`
    |         |
    |         arguments to this enum variant are incorrect
    |
    = note: expected enum `std::option::Option<std::boxed::Box<dyn tokio::io::AsyncRead + std::marker::Send>>`
               found enum `std::option::Option<std::boxed::Box<tokio::fs::File>>`
note: tuple variant defined here
   --> C:\Users\Fred\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\result.rs:508:5
    |
508 |     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
    |     ^^

But it works if I remove the Option<> part. Why?

Upvotes: 4

Views: 114

Answers (1)

Aplet123
Aplet123

Reputation: 35540

Essentially, your question can be boiled down to why one of these functions compiles while the other doesn't:

fn compiles() -> Box<dyn std::fmt::Debug> {
    let b: Box<&str> = Box::new("foo");
    b
}

fn doesnt_compile() -> Option<Box<dyn std::fmt::Debug>> {
    let b: Option<Box<&str>> = Some(Box::new("foo"));
    b
}

The key here is that Box<T> and Box<dyn Trait> are different types with different memory representations, even if T implements Trait. Normally, Rust is happy to implicitly cast Box<T> to Box<dyn Trait>, however this can't be done when the Box is inside another type since it's now a non-primitive cast (it's like why you can't cast Option<u8> to Option<u16>).

There are a couple ways of fixing this. You could try annotating res (let res: Option<Box<dyn AsyncRead + Send>> = ...), or casting the result of Box::new (let res = Some(Box::new(file) as Box<dyn AsyncRead + Send>)). In many cases, it's also possible to leave out the type entirely and just write let res = Some(Box::new(file) as _), but I'm not sure if this will work for this case.

Upvotes: 9

Related Questions