Using dyn trait with a hierarchy of marker traits - rust

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.

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.

Inherit trait from `From<&'_ [T]>` without specifying lifetime

TL;RD. I would like to inherit a trait from From like this: pub trait VectorLike<'a, T: 'a + Clone>: From<&'a [T]> {} but in such a way that VectorLike doesn't have a lifetime parameter.
Loner version. I'm writing generic code that supports various vector kinds (e.g. standard Vec and SmallVec from the smallvec crate). In order for my functions to work with different vectors I'm making a trait that encompasses everything that is common to them. It goes like this:
pub trait VectorLike<T: Clone>:
ops::Deref<Target = [T]> +
IntoIterator<Item = T> +
FromIterator<T> +
{
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
Everything above works fine.
However I run into problem when I try to support creating vector from slices like this: Vec::from(&[1, 2, 3][..]). In order to allow this Vec implements From<&'_ [T]> trait. So naturally I'm trying to add it as a parent trait for VectorLike:
pub trait VectorLike<T: Clone>:
// ...
From<&[T]> +
{
// ...
}
... and I get “expected named lifetime parameter” error. Rust compiler suggests to fix the code like this:
pub trait VectorLike<'a, T: 'a + Clone>:
// ...
From<&'a [T]> +
{
// ...
}
Now I have to specify explicit lifetime wherever VectorLike is used. This seems completely unnecessary:
This lifetime contains no valuable information: the vector does not inherit the lifetime, it copies all elements.
Lifetime specification of this sort is not required when Vec is used directly, e.g. this works: fn make_vector(elements: &[i32]) -> Vec<i32> { Vec::<i32>::from(elements) }.
I can workaround this limitation by adding a new function instead of implementing From: pub trait VectorLike<T: Clone>: /* ... */ { fn from_slice(s: &[T]) -> Self; }. This way my trait is functionally equivalent and can be used without lifetime specifiers.
So is there a way to remove the superfluous lifetime specifier here?
P.S. For now I'm using the “new function” option as a workaround, but it has drawbacks. For example, I find it confusing when the function has a different name, but giving it the same name leads to ambiguity which needs to be resolved with verbose constructs like this: <SmallVec::<[T; 8]> as From<&[T]>>::from(slice).
Every reference in Rust must have an associated lifetime.
The reason you usually don't have to write them out is because the compiler is very good at lifetime elision.
Whenever this doesn't work, you need to carefully consider where the lifetime you need is actually coming form.
In this case, the constraint you're trying to impose can be though of as
"for any lifetime 'a that you give me, VectorLike<T> implements From<&'a [T]>".
This is a higher-ranked trait bound that can be expressed using for<'a> syntax:
pub trait VectorLike<T: Clone>:
...
for<'a> From<&'a [T]>
{
...
}
Playground

Rust: additional trait bounds in conjunction with higher-ranked lifetime bounds

I want trait implementations in Rust to be able to return arbitrary iterators (of specific item type) that may reference the original object with a lifetime 'a without having to explicitly mention 'a in the trait generics and everywhere where the trait is used or otherwise introducing significant trait bound bloat to user code. The only simple way I've figured to do this is that the trait has to be implemented for &'a MyStruct instead of MyStruct (this approach is used in some places in the standard library), but the significant drawback is that in generic code wrappers cannot “own” implementations of the trait (MyStruct) without exposing the lifetime in trait bounds all over the code. So nothing gained when ownership is needed.
Another way I figured out that should work (just done the simple test below so far) is to use higher-ranked lifetime bounds on a “base trait” that would implement the iterator-generation functions. In the code below Foo is the main trait, FooInterfaceGen is the iterator-generator trait that has its lifetime “hidden” through for <'a> when assumed as a super-trait of Foo. The FooInterface generated by FooInterfaceGen would be the trait for an appropriate type of iterator when modified to that application of the idea. However, it seems impossible to make additional trait bounds on the specific implementation FooInterfaceGen::Interface. The code below works, but when you uncomment the Asdf trait bound in the function footest, the compiler complains that
the trait `for<'a> Asdf` is not implemented for `<_ as FooInterfaceGen<'a>>::Interface
But I have implemented Asdf! It's as if the compiler is ignoring the 'a in the expression <T as FooInterfaceGen<'a>> and just applying for<'a> to the right-hand-side. Any ideas if this is a compiler bug, a known restriction, or of any ways around it?
trait FooInterface<'a> {
fn foo(&self) -> u32;
}
trait FooInterfaceGen<'a> {
type Interface : FooInterface<'a>;
fn gen(&'a self) -> Self::Interface;
}
trait Foo : for<'a> FooInterfaceGen<'a> { }
struct S2;
struct S1(S2);
impl<'a> FooInterfaceGen<'a> for S1 {
type Interface = &'a S2;
fn gen(&'a self) -> Self::Interface { &self.0 }
}
impl Foo for S1 { }
impl<'a> FooInterface<'a> for &'a S2 {
fn foo(&self) -> u32 { 42 }
}
trait Asdf {}
impl<'a> Asdf for &'a S2 {}
fn footest<T : Foo>(a : &T) -> u32
/* where for<'a> <T as FooInterfaceGen<'a>>::Interface : Asdf */ {
a.gen().foo()
}
fn main() {
let q = S1(S2);
println!("{}", footest(&q));
}
(Regarding some alternative implementations, maybe there's a technical reason for it, but otherwise I really don't understand the reason behind the significant trait bound bloat that Rust code easily introduces. Assuming a trait should in any reasonable situation automatically assume all the trait bound as well, also in generic code, not just specific code, without having to copy-paste an increasing number of where-clauses all over the code.)
The error seems to be a known compiler bug: https://github.com/rust-lang/rust/issues/89196

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.

How to define an adapter trait where some implementations need a lifetime on &self?

I'm writing a set of benchmarks for different key-value stores, and would like to have a single adapter trait that I can use in all the benchmarks, and then implement it for each key-value store.
This worked well for two of them. However, the third required me to add a lifetime on my trait, and after fighting the borrow checker for an entire day, I still can't seem to get it right.
I've distilled it down to this minimal repro: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54fec74cb70c63c03f25ec7a9dfc7e60
What I don't understand is why the borrow on txn lives longer than the scope of benchmark(). It seems to me that it should live for only that one line.
How can I define the AdapterTransaction trait to resolve this, that still allows implementations to choose their own return type?
edit
added that I need to be able to use the AdapterTransaction implementations with a factory trait
The main problem in your first playground is the lifetime on &self being the same as the generic lifetime on the trait.
pub trait AdapterTransaction<'a, T: AsRef<[u8]>> {
fn get(&'a self, key: &[u8]) -> Option<T>;
}
Because they are the same, it requires the borrow of the underlying type to live at least as long as the type itself. This isn't true because even though the type is owned, the borrow would only last for the duration of the function call. In benchmark<'a,...>(), the lifetime 'a is picked by the caller, and there is no way a borrow within that function can be long enough. There would have been a quick fix to remove the 'a parameter on benchmark and replace it with a higher ranked trait bound (playground).
fn benchmark<U: AsRef<[u8]>, T: for<'a> AdapterTransaction<'a, U>>(txn: T)
In this example, 'a isn't chosen by the caller anymore, so the compiler is free to use a valid lifetime for the call.
As for the 2nd part of your question, traits can define associated types which can change depending on the implementation. You could have a trait that has an associated Output, which can change for each implemented type. There is a big difference with doing a generic param vs an associated type since in the former case, you are allowed to implement multiple generic variants of a trait for the same type. (This is how From<T> works, for example).
pub trait AdapterTransaction<'a> {
type Output;
fn get(&'a self, key: &[u8]) -> Option<Self::Output>;
}
impl<'a> AdapterTransaction<'a> for AdapterImpl {
type Output = &'a [u8];
fn get(&'a self, key: &[u8]) -> Option<Self::Output> {
Some(self.txn.get(&key))
}
}
fn benchmark<T>(txn: T)
where
for<'a> T: AdapterTransaction<'a>,
{
let _ = txn.get(&[]).unwrap();
}
Edit: Some of my initial assumptions weren't exact, it's not necessary to implement on &'a Type if the trait lifetime is used in a non-conflicting way.

Resources