Failure to apply turbofish notation on (boxed) trait objects - rust

I can create a Write trait object that points to a (heap-stored) File object as such:
let a : Box<dyn Write> = Box::new(File::open("/dev/null").unwrap()); // Compiles fine.
Using the turbofish notation, however, produces an error (Rust 1.56.1):
let a : Box<dyn Write> = Box::<dyn Write>::new(File::open("/dev/null").unwrap()); // Fails to compile.
The error being:
error[E0599]: the function or associated item `new` exists for struct `Box<dyn std::io::Write>`, but its trait bounds were not satisfied
--> src/main.rs:37:48
|
37 | let a : Box<dyn Write> = Box::<dyn Write>::new(File::open("/dev/null").unwrap()); // Fails to compile.
| ^^^ function or associated item cannot be called on `Box<dyn std::io::Write>` due to unsatisfied trait bounds
|
::: /home/[REDACTED]/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:1368:1
|
1368 | pub trait Write {
| --------------- doesn't satisfy `dyn std::io::Write: Sized`
|
= note: the following trait bounds were not satisfied:
`dyn std::io::Write: Sized`
I've been poking at the problem for the better part of a day. Either I'm missing something fundamental, or the special language treatment of Box is at play here (but I don't see how).

Your code is actually quite deceptive.
let a: Box<dyn Write> = Box::new(File::open("/dev/null").unwrap());
doesn't actually create a Box<dyn Write> like you think. It first creates a Box<File>, then the Box<File> is cast into a Box<dyn Write>. If you turbofish with File, then it works:
let a: Box<dyn Write> = Box::<File>::new(File::open("/dev/null").unwrap());
So, if you want to be explicit about the type, use an as cast instead of turbofish:
// a is Box<dyn Write>
let a = Box::new(File::open("/dev/null").unwrap()) as Box<dyn Write>;

Related

Why does &i32 sometimes implement Unpin and sometimes not?

use std::pin::Pin;
fn main() {
let val: i32 = 3;
let val_pin = Pin::new(&val);
f(val_pin);
f(Pin::new(&val));
}
fn f(_val: Pin<&dyn TestTrait>) {}
trait TestTrait {}
impl TestTrait for i32 {}
Line 9 f(Pin::new(&val)); throws a compiler error, but line 6 let val_pin = Pin::new(&val); does not. The compiler error is:
error[E0277]: `dyn TestTrait` cannot be unpinned
--> src/main.rs:9:7
|
9 | f(Pin::new(&val));
| ^^^^^^^^ the trait `Unpin` is not implemented for `dyn TestTrait`
|
= note: consider using `Box::pin`
note: required by a bound in `Pin::<P>::new`
--> /usr/lib/rustlib/src/rust/library/core/src/pin.rs:482:23
|
482 | impl<P: Deref<Target: Unpin>> Pin<P> {
| ^^^^^ required by this bound in `Pin::<P>::new`
So it seems that the trait Unpin is implemented for &val in line 6, but not in line 9. Why is that?
This has to do with some subtleties of type inference.
The variable on line 6 is inferred to be Pin<&i32> which later gets automatically upcast to Pin<&dyn TestTrait> on line 7. You can check this by inserting the following assignment after line 6, which forces a compile-time error that shows you the type of my_val:
let _: () = my_val;
= note: expected unit type `()`
found struct `Pin<&i32>`
(Side note: this won't work if my_val has type () -- then the assignment will succeed!)
In contrast, when you create the Pin as a temporary on line 9, it is inferred to be Pin<&dyn TestTrait> instead of Pin<&i32>, and &dyn TestTrait doesn't implement Unpin by default.
You can fix this by explicitly specifying the type argument T of Pin<T> to be &i32 on line 9:
f(Pin::<&i32>::new(&val));
You could also fix this by specifying a bound on TypeTrait of Unpin, which will cause &dyn TestTrait to implement Unpin as well:
trait TestTrait: Unpin {}
Finally, you could also fix the issue by changing f to accept Pin<&impl TestTrait> instead of Pin<&dyn TestTrait>:
fn f(_val: Pin<&impl TestTrait>) {}
As pointed out by #KevinReid, you can also indicate that the trait object must implement Unpin, which is allowed because Unpin has no methods:
fn f(_val: Pin<&(dyn TestTrait + Unpin)>) {}

Rust: can't collect an `Iterator<Item = Box<Dog>>` into `Vec<Box<dyn Animal>>`

I feel like this code should work since Box<Dog>s are supposed to be able to be implicitly converted into Box<dyn Animal>s in most situations:
struct Dog {}
trait Animal {}
impl Animal for Dog {}
fn main() {
let _: Vec<Box<dyn Animal>> = [Dog {}, Dog {}]
.into_iter()
.map(Box::new)
.collect();
}
However, I get the following compiler error:
error[E0277]: a value of type `Vec<Box<dyn Animal>>` cannot be built from an iterator over elements of type `Box<Dog>`
--> src/main.rs:9:10
|
9 | .collect();
| ^^^^^^^ value of type `Vec<Box<dyn Animal>>` cannot be built from `std::iter::Iterator<Item=Box<Dog>>`
|
= help: the trait `FromIterator<Box<Dog>>` is not implemented for `Vec<Box<dyn Animal>>`
= help: the trait `FromIterator<T>` is implemented for `Vec<T>`
note: required by a bound in `collect`
For more information about this error, try `rustc --explain E0277`.
I also tried inserting a .map(Into::into) to convert the Box<Dog>s into Box<dyn Animal>s, but that gives the error the trait bound `Box<dyn Animal>: From<Box<Dog>>` is not satisfied.
So how am I supposed to collect my Box<Dog>s into Box<dyn Animal>s?
You're almost there. The problem is that Box::new() takes T and gives Box<T>. No coercion is possible.
Instead, you should provide a closure |v| Box::new(v), but even that is not enough, because the compiler doesn't immediately realize it needs to coerce, and when it does it's too late (you can read more here). You need to hint it. A simple as will suffice: |v| Box::new(v) as _.

Why does Option<&str>::cloned() not produce an Option<String>?

I have an Option<&str> and I would like to end up with an Option<String>. For other types, Option::cloned would be an easy way to do that. The docs even say:
pub fn cloned(self) -> Option<T>
Maps an Option<&T> to an Option<T> by cloning the contents of the option.
However, trying this actually results in a compile error:
fn main() {
let unowned_str_maybe: Option<&str> = Some("abc");
let owned_str_maybe: Option<String> = unowned_str_maybe.cloned(); // <-- unsatisfied trait bounds
println!("{:?}", owned_str_maybe);
}
/* Compile error is:
error[E0599]: the method `cloned` exists for enum `Option<&str>`, but its trait bounds were not satisfied
--> src/main.rs:5:61
|
5 | let owned_str_maybe: Option<String> = unowned_str_maybe.cloned(); // <-- unsatisfied trait bounds
| ^^^^^^ method cannot be called on `Option<&str>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`str: Sized`
`str: Clone`
`Option<&str>: Iterator`
which is required by `&mut Option<&str>: Iterator`
*/
Using a more verbose (less idiomatic?) construction of .map(|x| x.to_string()) does compile. Why does Option::cloned not work for converting &str to String?
The reason is that Clone (which is used by Option::cloned) is used only to convert from &T to T, not from &T to U, even if semantically U is an owned version of &T (as String is to &str). In fact, you probably wouldn't expect this function to compile:
fn cloned(input: &str) -> String {
input.clone()
}
But Option::cloned is, essentially, .map(cloned), so it will work only if cloned works.
In fact, your approach with to_string is probably idiomatic. You probably could use function reference instead of closure, i.e. .map(ToString::to_string), or replace to_string with to_owned, but this is not so important.

Why the [u8] inside Box<[u8]> does not implement AsRef<[u8]> + AsMut<[u8]>? [duplicate]

This question already has an answer here:
Why does T implement A+B but not trait C: A+B?
(1 answer)
Closed 1 year ago.
The code below creates a Box<[u8]>, which I though had a fixed size, because Box has a fixed size. Also, I though [u8] implemented AsRef<[u8]> + AsMut<[u8]>
pub trait MemAs<T>: AsMut<[T]> + AsRef<[T]> {}
fn allocate(size: usize) -> Box<dyn MemAs<u8>> {
vec![0; size].into_boxed_slice()
}
Errors:
error[E0277]: the trait bound `[{integer}]: MemAs<u8>` is not satisfied
--> src/lib.rs:3:5
|
3 | vec![0; size].into_boxed_slice()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MemAs<u8>` is not implemented for `[{integer}]`
|
= note: required for the cast to the object type `dyn MemAs<u8>`
error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
--> src/lib.rs:3:5
|
3 | vec![0; size].into_boxed_slice()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[{integer}]`
= note: required for the cast to the object type `dyn MemAs<u8>`
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a348e149773b8145284fac308666b220
What is wrong?
First off, there are no impl blocks for MemAs. You probably meant to write a blanket impl:
impl<T, U> MemAs<T> for U
where
U: AsMut<[T]> + AsRef<[T]> + ?Sized {}
?Sized is needed here because [u8] is unsized.
However, adding this impl reveals a much more serious problem:
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> src/lib.rs:8:5
|
8 | vec![0; size].into_boxed_slice()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
= note: required for the cast to the object type `dyn MemAs<u8>`
This is saying you can't cast a [u8] to a dyn MemAs<u8>. A trait object (dyn Thing) can only refer to an object with a known size. Box<[u8]> is represented as a fat pointer (with the slice's length as auxiliary data), and Box<dyn MemAs<u8>> is also represented as a fat pointer (with a pointer to the vtable as auxiliary data), but there's no such thing as a "superfat" pointer in Rust (you can have a length or a vtable, but not both).
The workaround is to have two levels of Box:
fn allocate(size: usize) -> Box<dyn MemAs<u8>> {
Box::new(vec![0; size].into_boxed_slice())
}
The function's body evaluates to a Box<Box<[u8]>>, which can be coerced to a Box<dyn MemAs<u8>> because Box<[u8]> implements MemAs<u8>. (The ?Sized bound on the impl block is not needed for this to be true.)

A trait with a Sized supertrait still has the error "std::marker::Sized is not satisfied" [duplicate]

This question already has answers here:
Vector of objects belonging to a trait
(3 answers)
Closed 4 years ago.
I have the following code:
use std::collections::HashMap;
trait T: Sized {}
struct A;
impl T for A {}
fn main() {
let h: HashMap<String, T>;
}
But the compiler complains:
error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied
--> src\main.rs:10:12
|
10 | let h: HashMap<String, T>;
| ^^^^^^^^^^^^^^^^^^ `T` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `T`
= note: required by `std::collections::HashMap`
error[E0038]: the trait `T` cannot be made into an object
--> src\main.rs:10:12
|
10 | let h: HashMap<String, T>;
| ^^^^^^^^^^^^^^^^^^ the trait `T` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
I don't understand the error messages, because I've marked my trait T as Sized. Am I missing something?
because I've marked my trait T as Sized
No, you haven't. You've said that any type that implements T must be Sized. The trait itself is still unsized. You either need a trait object (e.g. Box<T>) or some kind of generic (which you cannot do in this context).

Resources