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>);
Related
In Rust for Rustaceans, Jon Gjengset states (emphasis mine):
When you’re given the choice between static and dynamic dispatch,
there is rarely a clear-cut right answer. Broadly speaking, though, you’ll
want to use static dispatch in your libraries and dynamic dispatch in your
binaries. In a library, you want to allow your users to decide what kind of
dispatch is best for them, since you don’t know what their needs are. If you use dynamic dispatch, they’re forced to do the same, whereas if you use
static dispatch, they can choose whether to use dynamic dispatch or not."
My interpretation of this was:
In a library one should use: fn execute(x: Arc<impl MyTrait>) -> ... This is static dispatch.
In a binary: let x: Arc<dyn MyTrait> = Arc::new(my_obj) This is dynamic.
Then I want to call execute(x), but the compiler complains:
error[E0277]: the size for values of type `dyn MyTrait` cannot be known at compilation time
--> src/main.rs:12:13
|
12 | execute(x);
| ------- ^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `dyn MyTrait`
What am I missing?
Update:
One of MyTrait's methods is:
fn build(conf: MyStruct) -> Self where Self: Sized; where I put Sized in order for this method to be "skipped". In other words, this method would never be called from execute function.
Your interpretation is correct. However, every generic argument in Rust is, unless explicitly stated otherwise, assumed to be Sized. So when you write
fn execute(x: Arc<impl MyTrait>) {}
That's equivalent[1] to
fn execute<T>(x: Arc<T>)
where T: MyTrait {}
And since we didn't opt out of the Sized constraint, this gives
fn execute<T>(x: Arc<T>)
where T: MyTrait + Sized {}
And while dyn MyTrait absolutely does implement MyTrait, it does not implement Sized. So if we write any of the following
fn execute(x: Arc<impl MyTrait + ?Sized>) {}
fn execute<T: MyTrait + ?Sized>(x: Arc<T>) {}
fn execute<T>(x: Arc<T>) where T: MyTrait + ?Sized {}
Then Rust will accept a trait object as argument to this function.
[1] Almost equivalent. You can't write execute::<MyType>() if you're using the impl syntax.
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.)
In Rust, references as well as Box<T>, Rc<T>, and Arc<T> allow the creation of a trait object (e.g., from Box<Type> to Box<dyn Trait>). But is there a way to allow the same conversion with a user-defined generic "smart pointer" type?
For example, MyBox<T> is a thin wrapper around Box<T> but the code below results in a compilation error:
use std::io::Write;
pub fn main() {
let std_box: Box<Vec<u8>> = Box::new(Vec::new());
let std_dyn: Box<dyn Write> = std_box;
// ^ this conversion is allowed.
let my_box: MyBox<Vec<u8>> = MyBox { t: Box::new(Vec::new()) };
let my_dyn: MyBox<dyn Write> = my_box;
// ^ this conversion is not allowed.
}
struct MyBox<T: ?Sized> {
t: Box<T>,
}
error[E0308]: mismatched types
--> traits/src/trait_objs.rs:7:36
|
7 | let my_dyn: MyBox<dyn Write> = my_box;
| ---------------- ^^^^^^ expected trait object `dyn std::io::Write`, found struct `Vec`
| |
| expected due to this
|
= note: expected struct `MyBox<dyn std::io::Write>`
found struct `MyBox<Vec<u8>>`
Unfortunately, it looks like the answer is "you don't" for Rust Stable. Rust is very conservative in which implicit coercions it allows. In particular, from the docs, we see the rule that's kicking in for Box.
Coercion is allowed between the following types:
...
TyCtor(T) to TyCtor(U), where TyCtor(T) is one of
&T
&mut T
*const T
*mut T
Box<T>
and where U can be obtained from T by unsized coercion.
where the relevant unsized coercion rule is
T to dyn U, when T implements U + Sized, and U is object safe.
There's not much room for special casing there, at least not in the current version of Rust.
However, if you're willing to dip into Nightly-only features, then you get the exciting CoerceUnsized trait, whose intended use case is... smart pointers to things which would coerce like a Box would.
#![feature(unsize, coerce_unsized)]
use std::ops::CoerceUnsized;
use std::marker::Unsize;
impl<T, U> CoerceUnsized<MyBox<U>> for MyBox<T> where T: Unsize<U>, U: ?Sized {}
This tells Rust we can coerce MyBox<T> to MyBox<U> if T is "basically the same" as U, for an appropriate definition of "basically the same". No functions are required on the implementation; the trait impl simply needs to exist.
When I make a composite trait (name?) that is made of two other traits, the compiler doesn't seem to detect that the two traits are implemented by that composite trait.
Here is the code:
use std::io::{Read, Seek};
use zip::read::ZipArchive;
trait ReadSeek: Read + Seek {}
impl<T: Read + Seek> ReadSeek for T {}
pub struct ArchiveZip<'a> {
path: &'a str,
archive: ZipArchive<&'a (dyn ReadSeek)>,
}
And here is the error:
error[E0277]: the trait bound `&'a (dyn ReadSeek + 'a): std::io::Read` is not satisfied
--> src/archive/zipfile.rs:12:14
|
12 | archive: ZipArchive<&'a (dyn ReadSeek)>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::io::Read` is not implemented for `&'a (dyn ReadSeek + 'a)`
|
::: /Users/nhooey/.cargo/registry/src/github.com-1ecc6299db9ec823/zip-0.5.11/src/read.rs:50:26
|
50 | pub struct ZipArchive<R: Read + io::Seek> {
| ---- required by this bound in `ZipArchive`
|
= note: `std::io::Read` is implemented for `&'a mut (dyn archive::ReadSeek + 'a)`, but not for `&'a (dyn archive::ReadSeek + 'a)`
Why does it not detect that the ReadSeek trait doesn't implement both Read and Seek?
(I omitted the other error message that is also complaining about Seek in the same way.)
From the error message you can see: struct ZipArchive<R: Read + io::Seek>.
This means that the generic argument (R) to ZipArchive must implement Read and io::Seek.
You code has: ZipArchive<&'a (dyn ReadSeek)>.
R in your code is therefore &'a (dyn ReadSeek) which is a reference (&'_ _). The fact that the object the reference points to implements Read and Seek doesn't change which traits the reference itself implements.
So, you can either implement your trait for references (in my experience this usually doesn't go well) or you find a way for R to be a type that directly implements the required traits.
Looking at the documentation for references, you can see that Read and Seek are implemented for mutable references. Changing you references to be mutable should therefore solve the problem.
I have a piece of code that does not compile and that can be reduced to this snippet:
use std::error::Error;
use std::convert::TryFrom;
// A trait that provides methods for parsing data into a type T.
pub trait Deserializable<T> {
// some methods
}
pub struct MyBuffer<'a> {
inner: &'a [u8]
}
impl<'a, T> Deserializable<T> for MyBuffer<'a>
where
T: TryFrom<&'a [u8]>,
<T as TryFrom<&'a [u8]>>::Error: Error + Sync + Send + 'static
{
// some methods to implement
}
fn main() {}
The compiler rejects this program with a confusing error message:
error[E0310]: the associated type `<T as std::convert::TryFrom<&[u8]>>::Error` may not live long enough
--> src/main.rs:13:13
|
13 | impl<'a, T> Deserializable<T> for MyBuffer<'a>
| ^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as std::convert::TryFrom<&[u8]>>::Error: 'static`...
note: ...so that the type `<T as std::convert::TryFrom<&[u8]>>::Error` will meet its required lifetime bounds
--> src/main.rs:13:13
|
13 | impl<'a, T> Deserializable<T> for MyBuffer<'a>
| ^^^^^^^^^^^^^^^^^
The error suggests that I add the 'static lifetime bound, but I already added it:
consider adding an explicit lifetime bound `<T as std::convert::TryFrom<&[u8]>>::Error: 'static`
Can someone explain why this program does not compile, and/or how to fix it (if possible)? It seems to me that it should be possible to have <T as TryFrom<&'a [u8]>>::Error to be 'static even though T itself is bound to 'a.
The reason I want Error to be 'static is that I use failure and failure::Fail is implemented for Send + Sync + Error + 'static.
This appears to be a gap in the compiler's ability to reason about lifetimes and associated types.
Sometimes, in order to help the compiler, you can add a generic parameter to alias an associated type. This parameter doesn't "count" in that it doesn't make the item "more generic", but because generics are resolved at use instead of declaration, it defers the hard part of type checking until the exact types are known. In other words: the compiler might be able to prove that any particular T::Error works, but it can't quite prove that every T::Error must work, so we introduce a new parameter E which is bound to be T::Error and tell the compiler to figure out what E is only when we try to use it.
The following works (playground):
impl<'a, T, E> Deserializable<T> for MyBuffer<'a>
where
T: TryFrom<&'a [u8], Error = E>,
E: Error + Sync + Send + 'static,
{
// ...
}
Instead of bounding T::Error, we introduce a new type parameter E with the bounds we want and constrain T such that its TryFrom::Error is E. This is logically (as far as I can tell) the same thing as what you wrote, but it compiles without complaint.
I can't find official documentation that refers to this; it may be an inherent limitation of the solver, or merely a bug.
I'm still not sure why, but it seems just removing the 'static bound on the error worked?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d4f46a0ad9a5dd7dc538fe4e197d823d