How do I specify lifetime parameters in an associated type? - rust

I have this trait and simple structure:
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec<PathBuf>,
}
I would like to implement the Foo trait for Bar:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
However I'm getting this error:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the self lifetime.
How do I have to modify the Foo trait, or the Bar trait implementation, to make this work?
Rust playground

There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
impl<'a> Foo<'a> for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}
When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(self) -> Self::Iter;
}
impl<'a> Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(self) -> Self::Iter {
self.v.iter()
}
}
Your fooget function now simply becomes
fn fooget<T: Foo>(foo: T) {}
The problem with this is that the fooget function doesn't know T is in reality a &Bar. When you call the get function, you are actually moving out of the foo variable. You don't move out of the object, you just move the reference. If your fooget function tries to call get twice, the function won't compile.
If you want your fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
The where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. It may also be implemented for both.
Technically, the compiler could automatically infer the lifetime in fooget_twice so you could write it as
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.

Use a wrapper type
If the trait and all its implementations are defined in one crate, a helper type can be useful:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
For a concrete type that implements Foo, implement the iterator conversion on the IterableFoo wrapping it:
impl Foo for Bar {}
impl<'a> IntoIterator for IterableFoo<'a, Bar> {
type Item = &'a PathBuf;
type IntoIter = std::slice::Iter<'a, PathBuf>;
fn into_iter(self) -> Self::IntoIter {
self.0.v.iter()
}
}
This solution does not allow implementations in a different crate. Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get:
fn use_foo_get<T>(foo: &T)
where
T: Foo,
for<'a> IterableFoo<'a, T>: IntoIterator,
for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
Associated type for an internal object providing desired functionality
The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits.
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
This requires that any implementation type contains a part that can be so exposed:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Put bounds on the reference to the associated type in generic code that uses the the result of get:
fn use_foo_get<'a, T>(foo: &'a T)
where
T: Foo,
&'a T::Iterable: IntoIterator,
<&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
This solution permits implementations outside of the trait definition crate.
The bound work at generic use sites is as annoying as with the previous solution.
An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.

In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet. See RFC 1598

Related

Rust - implementing trait for Deref: the parameter type `T` may not live long enough

I have a trait:
trait Foo {
fn bar(&self) -> Cow<str>;
}
And I want to implement it for any type that implements Deref with a target of a type that implements Foo. Basically:
impl<T: Foo, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(std::ops::Deref::deref(self))
}
}
Unfortunately, this gives the error the parameter type T may not live long enough.
My understanding is that T could have a reference within it that has a short lifetime, and the lifetime bound of the return value of Cow<str> is linked to the lifetime of &self due to lifetime elision, which would cause problems.
I'm not sure how I can fix this, since I'm not able to bound any of the lifetimes in bar. I can try to make sure T lives as long as &self, but this doesn't work.
impl<'a, T: Foo + 'a, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&'a self) -> Cow<'a, str> {
<T as Foo>::bar(std::ops::Deref::deref(self))
}
}
I get the error method not compatible with trait since the lifetimes don't match the trait defenition anymore. I've tried all sorts of different ways of adding lifetime bounds and I always get one of those two errors.
I am able to implement Foo for a specific type that implements Deref:
impl<T: Foo> Foo for Box<T> {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(self)
}
}
I'm not sure why that works but the original example doesn't.
The Box version works because of the deref coercion the compiler will do when it sees a reference and expects a different reference.
You can use the same mechanic when using a generic implementor of Deref to ensure that it Derefs to an owned type you can simply add a 'static lifetime bound on T like this:
impl<T: Foo + 'static, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(self)
}
}
playground
Note: there is rarely a need to call methods of std::ops traits directly, they're all just the methods behind Rusts operators, deref for example is the method behind unary *
Update:
Since there is an additional requirement that T might not be static we have to thread through the lifetime like you tried in your second example, like the error you're getting suggest you have to adjust the trait to take a lifetime as well:
use std::borrow::Cow;
trait Foo<'a> {
fn bar(&self) -> Cow<'a, str>;
}
impl<'a, T: Foo<'a>, D: std::ops::Deref<Target = T>> Foo<'a> for D {
fn bar(&self) -> Cow<'a, str> {
<T as Foo>::bar(self)
}
}
struct S<'a> {
val: &'a str,
}
impl<'a> Foo<'a> for S<'a> {
fn bar(&self) -> Cow<'a, str> {
todo!()
}
}
fn main() {
let val = String::from("test");
let s = S { val: &val }; // error: `val` does not live long enough
let b = Box::new(s);
let cow = Foo::bar(&b); // argument requires that `val` is borrowed for `'static`
}

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 to create an `Iterable` trait for references in Rust?

I'm trying to create a trait that captures the iter function in slice as well as VecDeque, BTreeMap and HashMap. I'd like the implementer of this trait to be able to specify and implement their own iterator type, but it looks like this iterator type must have a lifetime argument, and that cannot be given as an associated type.
In more detail, here's what I wish was possible in Rust:
trait RefIterable<T>
where for<'a> (T: 'a) => (Self::Iter<'a>: Iterator<Item = &'a T>)
{
type Iter; // Has kind (lifetime -> type)
fn refs<'a>(&'a self) -> Self::Iter<'a>
}
If this was possible, the implementation could look like this
impl RefIterable<T> for Vec<T> {
type Iter<'a> = std::slice::Iter<'a, T>; // This is not valid Rust code.
fn refs<'a>(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
I'm still relatively new to Rust, so I'm asking if there's already a way to do this that I'm not aware of, or if there's a nice workaround for this situation. I'd imagine that this situation is not very rare.
(Using Box<dyn 'a + Iterator<Item = &'a T>> is my current workaround, but that prevents some optimization from happening.)
Edit:
EvilTak's answer is probably the best thing we can do right now. The ability to combine all possible lifetimes together with the condition T: 'a into one unparametrized trait seems to be unsupported by Rust as of today.
Add the lifetime parameter to the trait instead, which allows you to use it in the associated type Iter's bound:
trait RefIterable<'a> {
type Item: 'a;
type Iter: Iterator<Item = &'a Self::Item>; // Has kind (lifetime -> type)
fn refs(&'a self) -> Self::Iter;
}
The Item: 'a bound is required to let the compiler know that the references (&'a Self::Item) do not outlive the type (Self::Item).
I have modified RefIterable to make it follow Iterator's convention of using an associated type to specify the type of the items that are iterated over for the same reason as the one behind Iterator's usage of an associated type.
Implementations are pretty straightforward:
impl<'a, T: 'a> RefIterable<'a> for Vec<T> {
type Item = T;
type Iter = std::slice::Iter<'a, T>;
fn refs(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
Playground

How can I convert dyn FnMut into a custom trait object? [duplicate]

In the code below it is not possible to obtain a reference to a trait object from a reference to a dynamically-sized type implementing the same trait. Why is this the case? What exactly is the difference between &dyn Trait and &(?Sized + Trait) if I can use both to call trait methods?
A type implementing FooTraitContainerTrait might e.g. have type Contained = dyn FooTrait or type Contained = T where T is a concrete type that implements FooTrait. In both cases it's trivial to obtain a &dyn FooTrait. I can't think of another case where this wouldn't work. Why isn't this possible in the generic case of FooTraitContainerTrait?
trait FooTrait {
fn foo(&self) -> f64;
}
///
trait FooTraitContainerTrait {
type Contained: ?Sized + FooTrait;
fn get_ref(&self) -> &Self::Contained;
}
///
fn foo_dyn(dyn_some_foo: &dyn FooTrait) -> f64 {
dyn_some_foo.foo()
}
fn foo_generic<T: ?Sized + FooTrait>(some_foo: &T) -> f64 {
some_foo.foo()
}
///
fn foo_on_container<C: FooTraitContainerTrait>(containing_a_foo: &C) -> f64 {
let some_foo = containing_a_foo.get_ref();
// Following line doesn't work:
//foo_dyn(some_foo)
// Following line works:
//some_foo.foo()
// As does this:
foo_generic(some_foo)
}
Uncommenting the foo_dyn(some_foo) line results in the compiler error
error[E0277]: the size for values of type `<C as FooTraitContainerTrait>::Contained` cannot be known at compilation time
--> src/main.rs:27:22
|
27 | foo_dyn(contained)
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `<C as FooTraitContainerTrait>::Contained`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where <C as FooTraitContainerTrait>::Contained: std::marker::Sized` bound
= note: required for the cast to the object type `dyn FooTrait`
This problem can be reduced to the following simple example (thanks to turbulencetoo):
trait Foo {}
fn make_dyn<T: Foo + ?Sized>(arg: &T) -> &dyn Foo {
arg
}
At first glance, it really looks like this should compile, as you observed:
If T is Sized, the compiler knows statically what vtable it should use to create the trait object;
If T is dyn Foo, the vtable pointer is part of the reference and can just be copied to the output.
But there's a third possibility that throws a wrench in the works:
If T is some unsized type that is not dyn Foo, even though the trait is object safe, there is no vtable for impl Foo for T.
The reason there is no vtable is because the vtable for a concrete type assumes that self pointers are thin pointers. When you call a method on a dyn Trait object, the vtable pointer is used to look up a function pointer, and only the data pointer is passed to the function.
However, suppose you implement a(n object-safe) trait for an unsized type:
trait Bar {}
trait Foo {
fn foo(&self);
}
impl Foo for dyn Bar {
fn foo(&self) {/* self is a fat pointer here */}
}
If there were a vtable for this impl, it would have to accept fat pointers, because the impl may use methods of Bar which are dynamically dispatched on self.
This causes two problems:
There's nowhere to store the Bar vtable pointer in a &dyn Foo object, which is only two pointers in size (the data pointer and the Foo vtable pointer).
Even if you had both pointers, you can't mix and match "fat pointer" vtables with "thin pointer" vtables, because they must be called in different ways.
Therefore, even though dyn Bar implements Foo, it is not possible to turn a &dyn Bar into a &dyn Foo.
Although slices (the other kind of unsized type) are not implemented using vtables, pointers to them are still fat, so the same limitation applies to impl Foo for [i32].
In some cases, you can use CoerceUnsized (only on nightly as of Rust 1.36) to express bounds like "must be coercible to &dyn FooTrait". Unfortunately, I don't see how to apply this in your case.
See also
What is a "fat pointer" in Rust?
Use trait object to pass str in rust has a concrete example of a reference to an unsized type (str) that cannot be coerced to a reference to a trait object.
Not sure if that solves your concrete problem, but I did solve mine with the following trick:
I added the following method to FooTrait:
fn as_dyn(&self) -> &dyn FooTrait;
A default impl can not be provided (because it requires that Self be Sized, but constraining FooTrait to be Sized forbids creating trait objects for it...).
However, for all Sized implementations, it is trivially implemented as
fn as_dyn(&self) -> &dyn FooTrait { self }
So basically it constrains all implementations of FooTrait to be sized, except for dyn FooTrait.
Try it on the playground
Referenced from this blog, which explains the fat pointer really well.
Thanks trentcl for simplifying the question to:
trait Foo {}
fn make_dyn<T: Foo + ?Sized>(arg: &T) -> &dyn Foo {
arg
}
This brings to how to cast between different ?Sized?
To answer this, let's first peek the implementation for Unsized type Trait.
trait Bar {
fn bar_method(&self) {
println!("this is bar");
}
}
trait Foo: Bar {
fn foo_method(&self) {
println!("this is foo");
}
}
impl Bar for u8 {}
impl Foo for u8 {}
fn main() {
let x: u8 = 35;
let foo: &dyn Foo = &x;
// can I do
// let bar: &dyn Bar = foo;
}
So, can you do let bar: &dyn Bar = foo;?
// below is all pseudo code
pub struct TraitObjectFoo {
data: *mut (),
vtable_ptr: &VTableFoo,
}
pub struct VTableFoo {
layout: Layout,
// destructor
drop_in_place: unsafe fn(*mut ()),
// methods shown in deterministic order
foo_method: fn(*mut ()),
bar_method: fn(*mut ()),
}
// fields contains Foo and Bar method addresses for u8 implementation
static VTABLE_FOO_FOR_U8: VTableFoo = VTableFoo { ... };
From the pseudo code, we can know
// let foo: &dyn Foo = &x;
let foo = TraitObjectFoo {&x, &VTABLE_FOO_FOR_U8};
// let bar: &dyn Bar = foo;
// C++ syntax for contructor
let bar = TraitObjectBar(TraitObjectFoo {&x, &VTABLE_FOO_FOR_U8});
The bar type is TraitObjectBar, which is not the type TraitObjectFoo. That is to say, you cannot assign a struct of one type to another different type (in rust, in C++ you can use reinterpret_cast).
What you can do it to have another level of indirection:
impl Bar for dyn Foo {
...
}
let bar: &dyn Bar = &foo;
// TraitObjectFoo {&foo, &VTABLE_FOO_FOR_DYN_FOO}
The same thing applies to Slice.
The workaround for casting different Unsized can be done by this trick:
// blanket impl for all sized types, this allows for a very large majority of use-cases
impl<T: Bar> AsBar for T {
fn as_bar(&self) -> &dyn Bar { self }
}
// a helper-trait to do the conversion
trait AsBar {
fn as_bar(&self) -> &dyn Bar;
}
// note that Bar requires `AsBar`, this is what allows you to call `as_bar`
// from a trait object of something that requires `Bar` as a super-trait
trait Bar: AsBar {
fn bar_method(&self) {
println!("this is bar");
}
}
// no change here
trait Foo: Bar {
fn foo_method(&self) {
println!("this is foo");
}
}

Trait which returns iterator with lifetime bounded by the lifetime of an argument

I have a trait which says that any implementation of Foo needs to provide a method bar which returns an object of some type which implements Iterator<Item = u32>:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(&self) -> FooIterator;
}
For this case, I believe that the default lifetime elision means that the iterator returned by bar is required to live on its own, without being tied to the lifetime of the Foo it is iterating over. User Habnabit on #rust irc suggested the following way to say that the lifetime of the FooIterator is less than the lifetime of the Foo. i.e. it allows the implementation of the FooIterator to keep a reference to the Foo that it comes from:
trait Foo<'a> {
type FooIterator: Iterator<Item = u32> + 'a;
fn bar<'b: 'a>(&'b self) -> Self::FooIterator;
}
What I really want is the case where the function bar takes an additional argument, and the implementation of FooIterator is allowed to keep a reference to both the Foo and the additional argument. i.e. the lifetime of FooIterator is bounded by the lifetime of the Foo and the lifetime of the additional argument.
My literal translation of this idea would be
trait Zip {}
trait Foo<'a, 'c> {
type FooIterator: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'b: 'a, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator;
}
But I was told there there is no "good" way to do this. What would be the best way to implement this idiom? What would the above code do exactly?
What you're looking for is associated type constructors, a planned feature that is not yet implemented in Rust. With associated type constructors, your code would look like this:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'b: 'a, 'c, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator<'a, 'c>;
}
Actually, I'm not sure all those lifetimes are necessary, because a &'a T can be coerced to a &'b T where 'a: 'b. Thus, the following might be good enough:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'c>(&'a self, &'c Zip) -> Self::FooIterator<'a, 'c>;
}
Depending on how you want to use this trait, you may be able to make it work by implementing it for &'a Struct instead of for Struct, thus "hoisting" the responsibility for finding the right lifetime from the trait into the caller.
Remove the lifetime annotation from the trait and change bar so it takes self, plus another argument of the same lifetime:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(self, other: Self) -> Self::FooIterator;
}
(Removing 'a from the trait is possible because bar consumes the reference instead of reborrowing it -- self doesn't have to outlive the return value anymore because it's been moved into it.)
Then impl it for a reference of lifetime 'a:
impl<'a> Foo for &'a Vec<u32> {
type FooIterator = ...; // something presumably containing 'a
fn bar(self, other: Self) -> Self::FooIterator {
...
}
}
This works because the compiler can limit the lifetime 'a to one for which the impl applies.
Here's a playground link where bar is basically a wrapper around .chain().
I'm ignoring the Zip trait for now because how to incorporate it depends on what it provides. Instead, I suppose that bar only accepts an argument of the same type as Self. However, you can probably add it as well, maybe using the same technique if you need to.

Resources