How do you set the lifetime of a Rust trait? - rust

I can set the lifetime for a Waypoint in the struct Route. In the struct AMoreDifferentRoute I use the trait Coord and get the error
explicit lifetime bound required
How do you set the lifetime of a trait in this case?
extern crate collections;
use super::wp;
use coord::Coord;
pub struct Route<'a> {
waypoints: &'a Vec<wp::Waypoint>
}
pub struct AMoreDifferentRoute<'a> {
waypoints: &'a Vec<Coord>
}

You write it as an additional trait bound:
pub struct AMoreDifferentRoute<'a> { // '
waypoints: &'a Vec<Box<Coord+'a>>
}
You need to specify a lifetime in Box<Coord+'a> because the trait can be implemented for a struct which has a lifetime parameter itself, so there should be a way to specify this lifetime parameter even if the actual struct type is hidden behind a trait object.
Moreover, you can't have bare Coord as its size is unknown, because Vec needs to know its components size to lay them out in memory properly. Hence you need some kind of wrapper to store trait objects. Box will do nicely.

As Coord is a trait, you need to box the value in some way as a trait object, such as Vec<Box<Coord>>.
You should also strongly consider whether this is actually what you want anyway; very often it’s not. But to make any sort of judgement on that, I’d need to see more code.

Related

How do I have a trait as a owned field of a struct?

I'm fairly new to Rust, I was tying to implement a struct that could have different structs that implemented a common trait as a field
In traditional programming languages with garbage collector I would have implemented it like this:
pub struct MemoryMapper {
regions: Vec<Region>,
}
trait MemoryMappable: Sized {
}
pub struct Region {
device: dyn MemoryMappable,
start: u32,
end: u32,
remap: bool
}
But here I have the following compilation error:
the size for values of type `(dyn MemoryMappable + 'static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn MemoryMappable + 'static)`
only the last field of a struct may have a dynamically sized type
change the field's type to have a statically known size
I have tried making Sized a supertrait of MemoryMappable, but it still gives me issues with the following code:
pub fn map(&mut self, device: dyn MemoryMappable, start: u32, end: u32, remap: bool) -> &Region {
self.regions.insert(0, Region{device, start, end, remap});
return self.regions.get(0).unwrap();
}
the trait `MemoryMappable` cannot be made into an object
`MemoryMappable` cannot be made into an object
Because from Rust documentation about object safety
Sized must not be a supertrait. In other words, it must not require Self: Sized.
I have no idea of how to go about this at this point
Fields of Sized structs can exist on the stack, and so must have a known size at compile time. This is what the Sized error is saying.
dyn Trait is "any type that implements Trait. So what is its size? Well, it depends on what the underlying type is.
Therefore, dyn Trait never implements Sized, even if Trait has Sized as a supertrait (all this does is guarantee that any type that implements Trait also implements Sized, but you still don't know which one it is).
In fact, making Sized a supertrait of Trait is how you opt-out of object safety, which makes it impossible to construct a dyn Trait.
Instead, the way you get around this is by using a pointer-like type:
Box<dyn Trait> creates an owned pointer
&dyn Trait creates a reference
Arc<dyn Trait> creates a reference counted pointer
etc.
The choice of pointer type depends on your use case, but in most scenarios, Box<dyn Trait> is fine, and is a good starting point.

Why is it necessary to specify associated types in trait objects

This code doesn't compile:
trait Trait {
type Type;
}
struct Struct;
impl Trait for Struct {
type Type = i32;
}
fn main() {
Box::new(Struct) as Box<dyn Trait>;
}
The fix is to change
Box::new(Struct) as Box<dyn Trait>;
into
Box::new(Struct) as Box<dyn Trait<Type = i32>>;
I want to know why we have to specify associated types in trait objects. Wouldn't compiler look for impl Trait for Struct and find type Type = i32?
Think about this from another side. You got Box<dyn Trait>. How can you know what is Trait::Type? Well, you cannot. Trait objects function as type erasure. And since you could depend on this associated type you must be sure you can name it.
If you for example got Vec<Box<dyn Trait>> and want to get Vec<Trait::Type> (assume that Trait::Type: Clone for the sake of this example) how can you require all trait object's to have the same associated type? The answer is in your question. You simply must specify it. Without it you would pretty fast run in the wall.

How to create a Vec of unit structs that implement sized trait?

I want to be able to create a Vec of unit structs that implement a sized trait (preferably without using a Box<T>).
Here is what I want to be able to do, but It fails and says that ProgrammingLanguage cannot be made into a trait object:
struct Python;
struct Rust;
trait ProgrammingLanguage: Sized {
fn say_hello_world() -> ();
}
impl ProgrammingLanguage for Python {...}
impl ProgrammingLanguage for Rust {...}
// Fails because ProgrammingLanguage cannot be made into a trait object
let foo = Vec::<ProgrammingLanguage>::from([Python, Rust]);
The Sized trait would not help you here. All it can guarantee is that any particular implementation of the trait is sized, i.e. that you can not implement it on [u8] or dyn OtherTrait; but the dyn ProgrammingLanguage itself is always unsized, by definition. The reason for this is quite simple: size of the trait object is defined as the size of an original type, and there's no way to statically guarantee that every type implementing the trait would have the same size.
However, there's no real reason to avoid Box. As was mentioned in the comments, Box<UnitStruct> is essentially just a never-dereferenced dangling pointer, without any allocation at all:
struct Rust;
fn main() {
println!("{:p}", Box::new(Rust)); // prints `0x1`
}
When you convert it to the trait object, pointer itself doesn't change - compiler just adds the vtable pointer, which is necessary for any operations with trait objects anyway.

Adding an iterator of a trait on another trait

I have a trait which has a function that should return an iterator of a different trait. I also need a common function which takes this trait and does operations on it.
This is the code I have currenty:
pub trait NodeLike : Sized {
/// Get the node id
fn get_id(&self) -> i32;
}
pub trait EdgeLike {
/// Get the destination node id
fn get_node_id(&self) -> i32;
/// Get the edge id
fn get_edge_id(&self) -> i32;
/// iterates of the source nodes
fn iter_source_nodes(&self) -> dyn Iterator<Item = dyn NodeLike>;
^^^^^^^^^^^^^
}
fn operation(id: i32, edge: dyn EdgeLike) {
for node in edge.iter_source_nodes().find(|n| n.get_id() == id) {
}
}
The underlined part above throws the compiler error:
the trait `NodeLike` cannot be made into an object
`NodeLike` cannot be made into an object rustc E0038
main.rs(1, 22): for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
Reading through the documentation, I believe my NodeLike trait is object-safe. The only function it has is &Self (i.e. &self)
Now, I can further my code by removing the Sized trait on NodeLike. However, then the error occurs in the function operation; I cannot use the find function on the iterator.
How can I change my code so that I can make NodeLike object safe, AND be able to return an iterator which I can call find on?
Following Stargateur's suggestion and your playground link, I got it to compile like this:
Playground link
This is if you do not want to use any unstable features.
The trick is to specify the explicit type of the iterator. If you don't know that type, just put any stupid type in and let the compiler complain about the type mismatch. It'll tell you that we're dealing here with a slice iter, so hence the std::slice::Iter.
The other modifications are then about adding a lifetime parameter to your struct, because iter() returns references. If you don't want that, change it to into_iter, but then you must use self instead of &self.
Also, for your operation, you must do some lifetime magic...
fn operation<'a, Item: NodeLike>(id: i32, edge: &'a impl EdgeLike<'a, Item=Item> )
If you are okay with unstable features, you can avoid having to specify the exact type of the iter: In your playground version just change the dyn to impl. The compiler will tell you that that's an unstable feature and will show you the macro to use.
Finally, to avoid having to add lifetime annotations to the trait itself, there's something about generic associated types, but maybe that's going too far for now.

Explicit lifetime declarations in trait objects held by structs

In the answer to this question there's a discussion of how to refer to trait objects held by structs which requires the following syntax:
struct Bar<'a> {
foo: &'a (Foo + 'a),
}
This is per RFC 438
Could I ask for more explanation of the double lifetime declaration? Levans said:
You have to specify the lifetime two times : once for the lifetime of
the reference, and once for the trait object itself, because traits
can be implemented for references, and if the underlying object is a
reference, you must specify its lifetime as well.
I understand the notion of a lifetime for the reference in the struct. But I don't understand why the lifetime isn't for the object that the trait is a trait on. Put another way, I don't know what it means to hold a reference for a trait without holding a reference to the underlying thing that it's a trait for.
Is there a case where the trait and the underlying object would have different lifetimes? What would it mean to hold onto a reference to a trait without holding on to the underlying thing the trait is on?
Asking yet another way, why can't Rust just do The Right Thing(tm) with:
struct Bar<'a> {
foo: &'a Foo,
}
where The Right Thing(tm) would be to interpret that as equivalent to the declaration above?
Sorry to bombard with questions, but I felt like I was doing something pretty basic (use a trait as a generic facet), and I had to go down the rabbit hole pretty deep, and I'd like to understand why the rabbit hole is that deep.
The error message: error: explicit lifetime bound required was decidedly unhelpful, because there is a lifetime bound already.
why the lifetime isn't for the object that the trait is a trait on
Because the reference to the trait object and the trait object itself might have different lifetimes. Here's an example of a trait that is implemented for a reference:
trait Quack {
fn quack(&self) { println!("Quack") }
}
impl<'a> Quack for &'a bool {}
struct MasterQuack<'a> {
q: &'a (Quack + 'a),
}
fn main() {
let a = true;
// a.quack(); // Nope, not defined
(&a).quack();
// MasterQuack {q: &a}; // Nope, this would be a reference to a boolean, which isn't Quack
MasterQuack {q: &&a};
}
One thing to note is that it's perfectly fine to have &'a (Trait + 'b) - that is, a reference to a trait that itself has a / is a reference, and those lifetimes are different. You said as much with
Is there a case where the trait and the underlying object would have different lifetimes?
But it's more a case of "the underlying object has references with different lifetimes".
why can't Rust just do The Right Thing(tm)
As of RFC 599 this now compiles:
struct Bar<'a> {
foo: &'a Foo,
}

Resources