Problem with trait object storing its reference elsewhere - rust

I want to have a method to_holder of SomeTrait that'll store reference to the trait object to a struct, but the compiler forces Self of method to_holder to be Sized. Annotating Self: Sized does work, but that way I can't call to_holder from a trait object, which is troubling.
Here's the code:
trait SomeTrait {
type Item;
fn to_holder(&self) -> TraitObjectHolder<Self::Item> {
TraitObjectHolder { obj: self }
}
}
struct TraitObjectHolder<'a, T> {
obj: &'a dyn SomeTrait<Item=T>,
}
struct SomeStruct<T> {
item: T
}
impl<T> SomeTrait for SomeStruct<T> {
type Item = T;
}
fn main() {
let some_struct = SomeStruct { item: 0 };
let some_struct = &some_struct as &dyn SomeTrait<Item=i32>;
some_struct.to_holder();
}
And here's the compiler error:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/main.rs:5:34
|
5 | TraitObjectHolder { obj: self }
| ^^^^ doesn't have a size known at compile-time
|
= note: required for the cast from `Self` to the object type `dyn SomeTrait<Item = <Self as SomeTrait>::Item>`
help: consider further restricting `Self`
|
4 | fn to_holder(&self) -> TraitObjectHolder<Self::Item> where Self: Sized {
| +++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
I wonder that I'm just passing around references here, what does that has to do with whether Self is Sized?
Actually, it seems if I don't use associated function, things will be fine
fn to_holder<T>(x: &dyn SomeTrait<Item=T>) -> TraitObjectHolder<T> {
TraitObjectHolder { obj: x }
}
Think you might need it, I'll also mention a bit of my original problem .
I'm writing a toy matrix calculating library, there is a trait Mat and many concrete types that implements Mat, for example, a DataMatrix holding the data and a SliceMatrix referring to slice of whatever matrix type that implements Mat.
Now, I want a method on Mat called row that returns a SliceMatrix referring to a row of that Mat trait object, but rust just doesn't allow me to call row on a trait object.

Related

expected trait object `dyn Responsability`, found type parameter `T`

I am trying to implement a responsability chain in Rust:
link to playground
use std::error::Error;
struct Query {
query: String,
}
struct Response {
response: u64,
}
trait Responsability {
fn take(&self, iterator: std::slice::Iter<Box<dyn Responsability>>, query: Query) -> Result<Response, Box<dyn Error>>;
}
struct ResponsabilityChain<T: Responsability> {
responsabilities: Vec<Box<T>>,
}
impl<T: Responsability> ResponsabilityChain<T>
where
T: Responsability,
{
pub fn new(responsabilities: Vec<T>) -> Self {
let responsabilities = responsabilities.into_iter()
.map(|elt| Box::new(elt))
.collect();
Self { responsabilities }
}
pub fn launch(&self, query: Query) -> Result<Response, Box<dyn Error>> {
let iterator = self.responsabilities.iter();
let responsability = iterator.next().unwrap();
responsability.take(iterator, query)
}
}
fn main() {
println!("Hello, world!");
}
The infamous message is:
Compiling playground v0.0.1 (/playground) error[E0308]: mismatched
types --> src/main.rs:35:29 | 19 | impl<T: Responsability>
ResponsabilityChain | - this type parameter ... 35 |
responsability.take(iterator, query) |
^^^^^^^^ expected trait object dyn Responsability, found type
parameter T | = note: expected struct std::slice::Iter<'_, Box<(dyn Responsability + 'static)>>
found struct std::slice::Iter<'_, Box<T>> = help: type parameters must be constrained to match other types = note:
for more information, visit
https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
For more information about this error, try rustc --explain E0308.
error: could not compile playground due to previous error
I do not understand why the compiler complains expecting Box<dyn Responsability> while having Box<T> since I specify T: Responsability. What do I do wrong?
dyn I and <T> where T: I are different types in Rust, so the compiler complains since there's no implicit conversion.
T is a concrete type determined at compile time. dyn I it is a "trait object", it is dynamic, and concrete type is unknown, but sort of carried within.
A good video on the topic.
Conversion from <T> where T: I to dyn I is not free, it has a runtime cost, so has to be explicit with the Rust's philosophy.
The code could be fixed by using Vec<Box<dyn Responsability>> in all places. It will also allow you passing arbitrary types to new(), which is probably what you want, because Vec<T> has to contain objects of the same type (remember that this type is determined at compile time).

Return of a trait that is implemented by two structs based on predefined string format [duplicate]

I'm trying to create a function that returns an instance of the Shader trait. Here is my drastically simplified code:
trait Shader {}
struct MyShader;
impl Shader for MyShader {}
struct GraphicsContext;
impl GraphicsContext {
fn create_shader(&self) -> Shader {
let shader = MyShader;
shader
}
}
fn main() {}
However I receive the following error:
error[E0277]: the trait bound `Shader + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:10:32
|
10 | fn create_shader(&self) -> Shader {
| ^^^^^^ `Shader + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Shader + 'static`
= note: the return type of a function must have a statically known size
Newer versions of the compiler have this error:
error[E0277]: the size for values of type `(dyn Shader + 'static)` cannot be known at compilation time
--> src/main.rs:9:32
|
9 | fn create_shader(&self) -> Shader {
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn Shader + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the return type of a function must have a statically known size
This makes sense as the compiler doesn't know the size of the trait, but nowhere can I find the recommended way of fixing this.
Passing back a reference with & wouldn't work as far as I know because the reference would outlive the lifetime of its creator.
Perhaps I need to use Box<T>?
Rust 1.26 and up
impl Trait now exists:
fn create_shader(&self) -> impl Shader {
let shader = MyShader;
shader
}
It does have limitations, such as not being able to be used in a trait method and it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.
Rust 1.0 and up
You need to return a trait object of some kind, such as &T or Box<T>, and you're right that &T is impossible in this instance:
fn create_shader(&self) -> Box<Shader> {
let shader = MyShader;
Box::new(shader)
}
See also:
What is the correct way to return an Iterator (or any other trait)?
Conditionally iterate over one of several possible iterators
I think this is what you were searching for; a simple factory implemented in Rust:
pub trait Command {
fn execute(&self) -> String;
}
struct AddCmd;
struct DeleteCmd;
impl Command for AddCmd {
fn execute(&self) -> String {
"It add".into()
}
}
impl Command for DeleteCmd {
fn execute(&self) -> String {
"It delete".into()
}
}
fn command(s: &str) -> Option<Box<Command + 'static>> {
match s {
"add" => Some(Box::new(AddCmd)),
"delete" => Some(Box::new(DeleteCmd)),
_ => None,
}
}
fn main() {
let a = command("add").unwrap();
let d = command("delete").unwrap();
println!("{}", a.execute());
println!("{}", d.execute());
}
I think you can use generics and static dispatch (I have no idea if those are the right terms, I just saw someone else use them) to create something like this.
This isn't exactly "returning as a trait", but it is letting functions use traits generically. The syntax is a little obscure, in my opinion, so it's easy to miss.
I asked Using generic iterators instead of specific list types about returning the Iterator trait. It gets ugly.
In the playground:
struct MyThing {
name: String,
}
trait MyTrait {
fn get_name(&self) -> String;
}
impl MyTrait for MyThing {
fn get_name(&self) -> String {
self.name.clone()
}
}
fn as_trait<T: MyTrait>(t: T) -> T {
t
}
fn main() {
let t = MyThing {
name: "James".to_string(),
};
let new_t = as_trait(t);
println!("Hello, world! {}", new_t.get_name());
}
return Box<shader>. As the size of the type must be fixed so you've to bound the object using box smart pointer.

implementing traits for dyn Fns

Today I was playing around with function traits. Though the example I show below might not practically be very useful, I do wonder why it doesn't compile.
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
Here I implement a trait for a function type. This function type is generic over it's parameter. This means that if you were to do it like this:
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<F, A> Other for F where F: (Fn(A)) + 'static {
fn do_something_other(&self) {
do_something(self);
}
}
you get an error stating a type parameter is unconstrained.
I get this and don't believe it's possible to do it with generics. But the dynamic approach, why doesn't it work? It gives the following error:
I don't understand this error. It states I pass a Fn(A) -> (), which doesn't implement Other. However, this error occurs literally in the implementation of Other. How can it not be implemented here?
My first thought was because each closure is its own type. If it has to do with this, I find the error very weird.
The first construction fails because you cannot convert a &dyn A into a &dyn B, even when implementing B for dyn A.
trait A {}
trait B {
fn do_thing(&self);
}
impl B for dyn A {
fn do_thing(&self) {
let b: &dyn B = self;
}
}
error[E0308]: mismatched types
--> src/lib.rs:9:25
|
9 | let b: &dyn B = self;
| ------ ^^^^ expected trait `B`, found trait `A`
| |
| expected due to this
|
= note: expected reference `&dyn B`
found reference `&(dyn A + 'static)`
Well, you can convert traits but only with help from the source trait. But since in this case the source is Fn, that's not a route.
The second construction fails because Rust won't let you implement traits that can conflict. Trying to implement B for a type that implements A<_> will automatically be rejected because types can have multiple implementations of A<_>.
trait A<T> {}
trait B {
fn do_thing(&self);
}
impl<T, U> B for T where T: A<U> {
fn do_thing(&self) {}
}
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:7:9
|
7 | impl<T, U> B for T where T: A<U> {
| ^ unconstrained type parameter
Regarding Fns in particular, its somewhat hard to tell since usually function objects only implement a single Fn trait. However, the keyword is usually since you can enable a feature on nightly to do just that. And the trait system usually doesn't play favorites.
So what can you do? Well the first method is still functional, just you have to keep the implementation within the trait. You can use the second method if you use a concrete types for the function arguments.
You can conceivably implement Other for &dyn Fn(_) (implementing it on the reference and not the object itself). But that's not particularly convenient with how Fn objects are usually used.
pub fn do_something(o: &dyn Other) {}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for &dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
fn main() {
// THIS WORKS
let closure: &dyn Fn(_) = &|x: i32| println!("x: {}", x);
closure.do_something_other();
// THIS DOESN'T WORK
// let closure = |x: i32| println!("x: {}", x);
// closure.do_something_other();
}
Another option would be to make the Other trait generic in order to constrain A, but that of course depends on how its designed to be used.

How do I return an instance of a trait from a method?

I'm trying to create a function that returns an instance of the Shader trait. Here is my drastically simplified code:
trait Shader {}
struct MyShader;
impl Shader for MyShader {}
struct GraphicsContext;
impl GraphicsContext {
fn create_shader(&self) -> Shader {
let shader = MyShader;
shader
}
}
fn main() {}
However I receive the following error:
error[E0277]: the trait bound `Shader + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:10:32
|
10 | fn create_shader(&self) -> Shader {
| ^^^^^^ `Shader + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Shader + 'static`
= note: the return type of a function must have a statically known size
Newer versions of the compiler have this error:
error[E0277]: the size for values of type `(dyn Shader + 'static)` cannot be known at compilation time
--> src/main.rs:9:32
|
9 | fn create_shader(&self) -> Shader {
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn Shader + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the return type of a function must have a statically known size
This makes sense as the compiler doesn't know the size of the trait, but nowhere can I find the recommended way of fixing this.
Passing back a reference with & wouldn't work as far as I know because the reference would outlive the lifetime of its creator.
Perhaps I need to use Box<T>?
Rust 1.26 and up
impl Trait now exists:
fn create_shader(&self) -> impl Shader {
let shader = MyShader;
shader
}
It does have limitations, such as not being able to be used in a trait method and it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.
Rust 1.0 and up
You need to return a trait object of some kind, such as &T or Box<T>, and you're right that &T is impossible in this instance:
fn create_shader(&self) -> Box<Shader> {
let shader = MyShader;
Box::new(shader)
}
See also:
What is the correct way to return an Iterator (or any other trait)?
Conditionally iterate over one of several possible iterators
I think this is what you were searching for; a simple factory implemented in Rust:
pub trait Command {
fn execute(&self) -> String;
}
struct AddCmd;
struct DeleteCmd;
impl Command for AddCmd {
fn execute(&self) -> String {
"It add".into()
}
}
impl Command for DeleteCmd {
fn execute(&self) -> String {
"It delete".into()
}
}
fn command(s: &str) -> Option<Box<Command + 'static>> {
match s {
"add" => Some(Box::new(AddCmd)),
"delete" => Some(Box::new(DeleteCmd)),
_ => None,
}
}
fn main() {
let a = command("add").unwrap();
let d = command("delete").unwrap();
println!("{}", a.execute());
println!("{}", d.execute());
}
I think you can use generics and static dispatch (I have no idea if those are the right terms, I just saw someone else use them) to create something like this.
This isn't exactly "returning as a trait", but it is letting functions use traits generically. The syntax is a little obscure, in my opinion, so it's easy to miss.
I asked Using generic iterators instead of specific list types about returning the Iterator trait. It gets ugly.
In the playground:
struct MyThing {
name: String,
}
trait MyTrait {
fn get_name(&self) -> String;
}
impl MyTrait for MyThing {
fn get_name(&self) -> String {
self.name.clone()
}
}
fn as_trait<T: MyTrait>(t: T) -> T {
t
}
fn main() {
let t = MyThing {
name: "James".to_string(),
};
let new_t = as_trait(t);
println!("Hello, world! {}", new_t.get_name());
}
return Box<shader>. As the size of the type must be fixed so you've to bound the object using box smart pointer.

What is 'core::kinds::Sized` is not implemented for the type `Self' in rust?

This used to work:
struct Foo<'a, T> {
parent:&'a (Array<T> + 'a)
}
impl<'a, T> Foo<'a, T> { //'
pub fn new<T>(parent:&Array<T>) -> Foo<T> {
return Foo {
parent: parent
};
}
}
trait Array<T> {
fn as_foo(&self) -> Foo<T> {
return Foo::new(self);
}
}
fn main() {
}
Now it errors:
:15:21: 15:25 error: the trait core::kinds::Sized is not implemented for the type Self
:15 return Foo::new(self);
I can kind of guess what's wrong; it's saying that my impl of Foo<'a, T> is for T, not Sized? T, but I'm not trying to store a Sized? element in it; I'm storing a reference to a Sized element in it. That should be a pointer, fixed size.
I don't see what's wrong with what I'm doing, or why it's wrong?
For example, I should (I think...) be able to store a &Array in my Foo, no problem. I can't see any reason this would force my Foo instance to be unsized.
playpen link: http://is.gd/eZSZYv
There's two things going on here: trait objects coercions (the error), and object safety (fixing it).
The error
As suggested by the error message, the difficult part of the code is the Foo::new(self), and this is because pub fn new<T>(parent: &Array<T>) -> ..., that is, self is being coerced to an &Array<T> trait object. I'll simplify the code to:
trait Array {
fn as_foo(&self) {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
which gives the same thing:
<anon>:3:13: 3:27 error: the trait `core::kinds::Sized` is not implemented for the type `Self`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~~~~~~~~~~~
Self is the stand-in name for the type that implements the trait. Unlike most generic parameters, Self is possibly-unsized (?Sized) by default, since RFC 546 and #20341 for the purposes of allowing e.g. impl Array<T> for Array<T> to work by default more often (we'll come to this later).
The variable self has type &Self. If Self is a sized type, then this is a normal reference: a single pointer. If Self is an unsized type (like [T] or a trait), then &Self (&[T] or &Trait) is a slice/trait object: a fat pointer.
The error appears because the only references &T that can be cast to a trait object are when T is sized: Rust doesn't support making fat pointers fatter, only thin pointer → fat pointer is valid. Hence, since the compiler doesn't know that Self will always be Sized (remember, it's special and ?Sized by default) it has to assume the worst: that the coercion is not legal, and so it's disallowed.
Fixing it
It seems logical that the fix we're looking for is to ensure that Self: Sized when we want to do a coercion. The obvious way to do this would be to make Self always Sized, that is, override the default ?Sized bound as follows:
trait Array: Sized {
fn as_foo(&self) {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
Looks good!
Except there's the small point that it doesn't work; but at least it's for a difference reason, we're making progress! Trait objects can only be made out of traits that are "object safe" (i.e. safe to be made into a trait object), and having Sized Self is one of the things that breaks object safety:
<anon>:3:13: 3:17 error: cannot convert to a trait object because trait `Array` is not object-safe [E0038]
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
(I filed the double printing of the note as #20692.)
Back to the drawing board. There's a few other "easy" possibilities for a solution:
define an extension trait trait ArrayExt: Sized + Array { fn as_foo(&self) { ... } } and implement it for all Sized + Array types
just use a free function fn array_as_foo<A: Array>(x: &A) { ... }
However, these don't necessarily work for every use case, e.g. specific types can't customise the behaviour by overloading the default method. However, fortunately there is a fix!
The Turon Trick
(Named for Aaron Turon, who discovered it.)
Using generalised where clauses we can be highly specific about when Self should implement Sized, restricting it to just the method(s) where it is required, without infecting the rest of the trait:
trait Array {
fn as_foo(&self) where Self: Sized {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
This compiles just fine! By using the where clause like this, the compiler understands that (a) the coercion is legal because Self is Sized so self is a thin pointer, and (b) that the method is illegal to call on a trait object anyway, and so doesn't break object safety. To see it being disallowed, changing the body of as_foo to
let x = self as &Array; // coerce to a trait object
x.as_foo();
gives
<anon>:4:7: 4:15 error: the trait `core::kinds::Sized` is not implemented for the type `Array`
<anon>:4 x.as_foo();
^~~~~~~~
as expected.
Wrapping it all up
Making this change to the original unsimplified code is as simple adding that where clause to the as_foo method:
struct Foo<'a, T> { //'
parent:&'a (Array<T> + 'a)
}
impl<'a, T> Foo<'a, T> {
pub fn new(parent:&Array<T>) -> Foo<T> {
return Foo {
parent: parent
};
}
}
trait Array<T> {
fn as_foo(&self) -> Foo<T> where Self: Sized {
return Foo::new(self);
}
}
fn main() {
}
which compiles without error. (NB. I had to remove the unnecessary <T> in pub fn new<T> because that was causing inference failures.)
(I have some in-progress blog posts that go into trait objects, object safety and the Turon trick, they will appear on /r/rust in the near future: first one.)

Resources