Reputation: 4136
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
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