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

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.

Related

Using dyn trait with a hierarchy of marker traits

I'm using the open source rust pcap library (https://github.com/rust-pcap/pcap) whose main Capture struct uses a system of market traits to denote which functions are available during which lifecycle state/phase of the capture (e.g., you can't 'sendpacket' on a Capture in the "DEAD" state). It has a hierarchy of State traits such that, e.g., "Activated" is a trait that is implemented by both the "Active", "Dead" and "Offline" states:
pub trait Activated: State {}
impl Activated for Active {}
impl Activated for Offline {}
impl Activated for Dead {}
pub trait State {}
impl State for Inactive {}
impl State for Active {}
impl State for Offline {}
impl State for Dead {}
pub struct Capture<T: State + ?Sized> {
...
}
With that code, I am trying unsuccessfully to convert from a parameterized+bounded trait as a <T: Activated + ?Sized> to a Capture, e.g., :
fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
bar(capture as Capture<dyn Activated>);
}
fn bar(&mut capture: Capture<dyn Activated>) {
...
}
... but am getting an error:
non-primitive cast: &mut pcap::Capture<T> as &mut pcap::Capture<dyn pcap::Activated> as expression can only be used to convert between primitive types or to coerce to a specific trait object
... which I don't understand at all, despite a fair bit of reading on this. Can someone please explain to me how to do this (or why it's wrong to think this should work)? Thank you in advance!
In current Rust, you can only coerce from Foo<T> to Foo<dyn Trait> if T is a sized type. This is because the conversion requires attaching a vtable pointer for Trait, and said vtable is only available when T is a regular, non-dyn type. So, this code,
fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
bar(capture as Capture<dyn Activated>);
}
will always fail to compile because T: ?Sized, meaning that T might be already a dyn, and you can't cast from a dyn to an arbitrary different dyn.
There is an unstable feature, trait_upcasting (#65991) that will enable dyn conversions to supertraits, like Capture<dyn Activated> to Capture<dyn State>, but that still does not include conversion from an arbitrary T: ?Sized. (In fact, if T = [Foo], a type that is dynamically sized for a different reason, then it's impossible for an entirely different reason than traits — the compiler does not support a type being simultaneously vtable-based (having a pointer) and a slice (having a length). That restriction could in principle be lifted, but it'd be more new territory.)
To recap: You will need to organize your program so that you do not need to convert a ?Sized type to a trait object, because that is not possible.

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.

Why is the `Sized` bound necessary in this trait?

I have a trait with two associated functions:
trait WithConstructor: Sized {
fn new_with_param(param: usize) -> Self;
fn new() -> Self {
Self::new_with_param(0)
}
}
Why does the default implementation of the second method (new()) force me to put the Sized bound on the type? I think it's because of the stack pointer manipulation, but I'm not sure.
If the compiler needs to know the size to allocate memory on the stack,
why does the following example not require Sized for T?
struct SimpleStruct<T> {
field: T,
}
fn main() {
let s = SimpleStruct { field: 0u32 };
}
As you probably already know, types in Rust can be sized and unsized. Unsized types, as their name suggests, do not have a size required to store values of this type which is known to the compiler. For example, [u32] is an unsized array of u32s; because the number of elements is not specified anywhere, the compiler doesn't know its size. Another example is a bare trait object type, for example, Display, when it is used directly as a type:
let x: Display = ...;
In this case, the compiler does not know which type is actually used here, it is erased, therefore it does not know the size of values of these types. The above line is not valid - you can't make a local variable without knowing its size (to allocate enough bytes on the stack), and you can't pass the value of an unsized type into a function as an argument or return it from one.
Unsized types can be used through a pointer, however, which can carry additional information - the length of available data for slices (&[u32]) or a pointer to a virtual table (Box<SomeTrait>). Because pointers always have a fixed and known size, they can be stored in local variables and be passed into or returned from functions.
Given any concrete type you can always say whether it is sized or unsized. With generics, however, a question arises - is some type parameter sized or not?
fn generic_fn<T>(x: T) -> T { ... }
If T is unsized, then such a function definition is incorrect, as you can't pass unsized values around directly. If it is sized, then all is OK.
In Rust all generic type parameters are sized by default everywhere - in functions, in structs and in traits. They have an implicit Sized bound; Sized is a trait for marking sized types:
fn generic_fn<T: Sized>(x: T) -> T { ... }
This is because in the overwhelming number of times you want your generic parameters to be sized. Sometimes, however, you'd want to opt-out of sizedness, and this can be done with ?Sized bound:
fn generic_fn<T: ?Sized>(x: &T) -> u32 { ... }
Now generic_fn can be called like generic_fn("abcde"), and T will be instantiated with str which is unsized, but that's OK - this function accepts a reference to T, so nothing bad happens.
However, there is another place where question of sizedness is important. Traits in Rust are always implemented for some type:
trait A {
fn do_something(&self);
}
struct X;
impl A for X {
fn do_something(&self) {}
}
However, this is only necessary for convenience and practicality purposes. It is possible to define traits to always take one type parameter and to not specify the type the trait is implemented for:
// this is not actual Rust but some Rust-like language
trait A<T> {
fn do_something(t: &T);
}
struct X;
impl A<X> {
fn do_something(t: &X) {}
}
That's how Haskell type classes work, and, in fact, that's how traits are actually implemented in Rust at a lower level.
Each trait in Rust has an implicit type parameter, called Self, which designates the type this trait is implemented for. It is always available in the body of the trait:
trait A {
fn do_something(t: &Self);
}
This is where the question of sizedness comes into the picture. Is the Self parameter sized?
It turns out that no, Self is not sized by default in Rust. Each trait has an implicit ?Sized bound on Self. One of the reasons this is needed because there are a lot of traits which can be implemented for unsized types and still work. For example, any trait which only contains methods which only take and return Self by reference can be implemented for unsized types. You can read more about motivation in RFC 546.
Sizedness is not an issue when you only define the signature of the trait and its methods. Because there is no actual code in these definitions, the compiler can't assume anything. However, when you start writing generic code which uses this trait, which includes default methods because they take an implicit Self parameter, you should take sizedness into account. Because Self is not sized by default, default trait methods can't return Self by value or take it as a parameter by value. Consequently, you either need to specify that Self must be sized by default:
trait A: Sized { ... }
or you can specify that a method can only be called if Self is sized:
trait WithConstructor {
fn new_with_param(param: usize) -> Self;
fn new() -> Self
where
Self: Sized,
{
Self::new_with_param(0)
}
}
Let's see what would happen if you did this with an unsized type.
new() moves the result of your new_with_param(_) method to the caller. But unless the type is sized, how many bytes should be moved? We simply cannot know. That's why move semantics require Sized types.
Note: The various Boxes have been designed to offer runtime services for exactly this problem.

How do you set the lifetime of a Rust trait?

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.

Resources