Why does &i32 sometimes implement Unpin and sometimes not? - rust

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)>) {}

Related

Failure to apply turbofish notation on (boxed) trait objects

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>;

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.)

Type mismatch with iterator of traits as function argument

I have a function that accepts a trait as argument wrapped in a Box
use std::fmt::Display;
fn push(e: Box<dyn Display>) {}
push(Box::new(0));
Now I'd like to create another function that accepts an iterator where each Item is of the same type
fn push_batch<I>(batch: I)
where
I: IntoIterator<Item = Box<dyn Display>>,
{
}
push_batch((0..10).map(|i| Box::new(i)));
But doing so results in the following error:
error[E0271]: type mismatch resolving `<[closure#src/main.rs:13:28: 13:43] as FnOnce<({integer},)>>::Output == Box<(dyn std::fmt::Display + 'static)>`
--> src/main.rs:13:5
|
5 | fn push_batch<I>(batch: I)
| ---------- required by a bound in this
6 | where
7 | I: IntoIterator<Item = Box<dyn Display>>,
| ----------------------- required by this bound in `push_batch`
...
13 | push_batch((0..10).map(|i| Box::new(i)));
| ^^^^^^^^^^ expected integer, found trait object `dyn std::fmt::Display`
|
= note: expected struct `Box<{integer}>`
found struct `Box<(dyn std::fmt::Display + 'static)>`
= note: required because of the requirements on the impl of `Iterator` for `Map<std::ops::Range<{integer}>, [closure#src/main.rs:13:28: 13:43]>`
Why does the first example compile while the second doesn't? And how can I create an iterator of boxed traits to pass to the push_batch function?
Rust does not automatically cast Box<T> to Box<dyn Trait>, you must do it explicitly:
push_batch((0..10).map(|i| Box::new(i) as Box<dyn Display>));

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).

Rust compiler does not see structure as Sized

I am trying to define a trait as follows:
pub struct Parameter<A: Parameterisable>(&'static str, Box<A>);
pub trait Parameterisable {
// Some functions
}
impl Parameterisable for i32 {}
impl Parameterisable for f64 {}
pub struct ParameterCollection(Vec<Parameter<Parameterisable>>);
That is, the parameter collection is a mixture of parameters of different types. However compiling gives the following error:
error[E0277]: the trait bound `Parameterisable + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:10:32
|
10 | pub struct ParameterCollection(Vec<Parameter<Parameterisable>>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `Parameterisable + 'static: std::marker::Sized` not satisfied
|
= note: `Parameterisable + 'static` does not have a constant size known at compile-time
= note: required by `Parameter`
I am aware from this post that Vec must be Sized, but it seems that Parameter should be sized (because of the Box) so how do I convince the Rust compiler that Parameter is a Sized type?
Type parameters in Rust have an implicit Sized bound unless otherwise specified (by adding a ?Sized bound).
So the Parameter struct declaration is effectively:
pub struct Parameter<A: Parameterisable+Sized>(&'static str, Box<A>);
Note that Parameter<T> is always itself sized, since &'static str and Box<A> are always sized; the bound just says that T must also be sized.
The error message backs this up; it's saying that Parameterisable is not Sized, not that Parameter<Parametrerisable> is not Sized.
So the correct change is to add the ?Sized bound:
pub struct Parameter<A: Parameterisable+?Sized>(&'static str, Box<A>);

Resources