Using higher-ranked trait bounds with generics - rust

I've stumbled upon an interesting edge case: using higher-ranked lifetime bounds to accept closures that return generic parameters, such as for<'a> FnOnce(&'a T) -> R: MyTrait. There's no way to specify that R lives for at most 'a. Perhaps it's best to explain with an example.
Let's define a simple reference-like type wrapping a value:
struct Source;
struct Ref<'a> {
source: &'a Source,
value: i32,
}
For convenience, let's add a helper constructor. Here I will use explicit lifetimes to make the borrowing self-explanatory:
impl Source {
fn new_ref<'a>(&'a self, value: i32) -> Ref<'a> {
Ref { source: self, value }
}
}
This is an extremely fancy implementation of a integer copying routine using HRTBs with a closure over our Ref:
fn call_1<F>(callback: F) -> i32
where
for<'a> F: FnOnce(&'a Source) -> Ref<'a>,
{
let source = Source;
callback(&source).value
}
fn fancy_copy_1(value: i32) -> i32 {
call_1(|s| s.new_ref(value))
}
This is fine and is working as expected. We know that Ref does not outlive the Source and the compiler is also able to pick it up. Now let's create a simple trait and implement it for our reference:
trait MyTrait {
fn value(&self) -> i32;
}
impl<'a> MyTrait for Ref<'a> {
fn value(&self) -> i32 {
self.value
}
}
And modify our integer copying routine to return a generic type implementing that trait instead of just returning Ref:
fn call_2<R, F>(callback: F) -> i32
where
for<'a> F: FnOnce(&'a Source) -> R,
R: MyTrait,
{
let source = Source;
callback(&source).value()
}
fn fancy_copy_2(value: i32) -> i32 {
call_2(|s| s.new_ref(value))
}
Suddenly I get an error: cannot infer an appropriate lifetime for autoref due to conflicting requirements. Rust playground link for convenience. That actually makes sense from some perspective: unlike with Ref<'a> in the first example I never said that R has to live for at most 'a. could very well live longer and thus have access to freed memory. So I need to annotate it with it's own lifetime. But there's no place to do it! The first instinct is to put the lifetime in the bounds:
where
for<'a> F: FnOnce(&'a Source) -> R,
R: MyTrait + 'a,
which is of course incorrect, as 'a is only defined for the first bound.
This is where I got confused, started searching and never found anything about combining HRTBs and generic types in one bound. Perhaps more experienced people in Rust have any suggestions?
Upd 1.
While I was thinking about the problem some more, I remembered I could use the impl Trait syntax. This looks like a solution to my problem:
fn call_3<F>(callback: F) -> i32
where
F: for<'a> FnOnce(&'a Source) -> (impl MyTrait + 'a),
{
let source = Source;
callback(&source).value()
}
fn fancy_copy_3(value: i32) -> i32 {
call_3(|s| Box::new(s.new_ref(value)))
}
That, however does not work because impl MyTrait is not allowed in this place for some reason (probably temporarily). But that made me think of dyn Trait syntax and that indeed does work!
fn call_4<F>(callback: F) -> i32
where
F: for<'a> FnOnce(&'a Source) -> Box<dyn MyTrait + 'a>,
{
let source = Source;
let value = callback(&source); // note how a temporary is required
value.value()
}
fn fancy_copy_4(value: i32) -> i32 {
call_4(|s| Box::new(s.new_ref(value)))
}
Here's my solution: with dyn Trait syntax there is a place to put the + 'a! Unfortunately this solution still doesn't quite work for me too well, as it requires object-safety on the trait plus adds an overhead of allocating a boxed value. But at least it's something.

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`
}

Patterns for proxying and implementation hiding in Rust?

So I'm an experienced developer, pretty new to Rust, expert in Java - but started out in assembly language, so I get memory and allocation, and have written enough compiler-y things to fathom the borrow-checker pretty well.
I decided to port a very useful, high-performance bitset-based graph library I wrote in Java, both to use it in a larger eventual project, and because it's darned useful. Since it's all integer positions in bitsets, and if you want an object graph you map indices into an array or whatever - I'm not tangled up in building a giant tree of objects that reference each other, which would be a mess to try to do in Rust. The problem I'm trying to solve is much simpler - so simple I feel like I must be missing an obvious pattern for how to do it:
Looking over the various bit-set libraries available for Rust, FixedBitSet seemed like a good fit. However, I would rather not expose it via my API and tie every consumer of my library irrevocably to FixedBitSet (it is nice, but, it can also be useful to swap in, say, an implementation backed by atomics; and being married to usize may not be ideal, but FixedBitSet is).
In Java, you'd just create an interface that wraps an instance of the concrete type, and exposes the functionality you want, hiding the implementation type. In Rust, you have traits, so it's easy enough to implement:
pub trait Bits<'i, S: Sized + Add<S>> {
fn size(&'i self) -> S;
fn contains(&'i self, s: S) -> bool;
...
impl<'i, 'a> Bits<'i, usize> for FixedBitSet {
fn size(&'i self) -> usize {
self.len()
}
fn contains(&'i self, s: usize) -> bool {
FixedBitSet::contains(self, s)
}
...
this gets a little ugly in that, if I don't want to expose FixedBitSet, everything has to return Box<dyn Bits<'a, usize> + 'a>, but so be it for now - though that creates its own problems with the compiler not knowing the size of dyn Bits....
So far so good. Where it gets interesting (did I say this was a weirdly simple problem?) is proxying an iterator. Rust iterators seem to be irrevocably tied to a concrete Trait type which has an associated type. So you can't really abstract it (well, sort of, via Box<dyn Iterator<Item=usize> + 'a> where ..., and it looks like it might be possible to create a trait that extends iterator and also has a type Item on it, and implement it for u32, u64, usize and ?? maybe the compiler coalesces the type Item members of the traits?). And as far as I can tell, you can't narrow the return type in a trait implementation method to something other than the trait specifies, either.
This gets further complicated by the fact that the Ones type in FixedBitSet for iterating set bits has its own lifetime - but Rust's Iterator does not - so any generic Iterator implementation is going to need to return an iterator scoped to that lifetime, not '_ or there will be issues with how long the iterator lives vis-a-vis the thing that created it.
The tidiest thing I could come up with - which is not tidy at all - after experimenting with various containers for an iterator (an implementation of Bits that adds an offset to the base value is also useful) that expose it, was something like:
pub trait Biterable<'a, 'b, S: Sized + Add<S>, I: Iterator<Item = S> + 'b> where 'a: 'b {
fn set_bits<'c>(&'a mut self) -> I where I: 'c, 'b: 'c;
}
which is implementable enough:
impl<'a, 'b> Biterable<'a, 'b, usize, Ones<'b>> for FixedBitSet where 'a: 'b {
fn set_bits<'c>(&'a mut self) -> Ones<'b> where Ones<'b>: 'c, 'b: 'c {
self.ones()
}
}
but then, we know we're going to be dealing with Boxes. So, we're going to need an implementation for that. Great! A signature like impl<'a, 'b> Biterable<'a, 'b, usize, Ones<'b>> for Box<FixedBitSet> where 'a: 'b { is implementable. BUUUUUUT, that's not what anything is going to return if we're not exposing FixedBitSet anywhere - we need it for Box<dyn Bits<...> + ...>. For that, we wind up in a hall of mirrors, scribbling out increasingly baroque and horrifying (and uncompilable) variants on
impl<'a, 'b, B> Biterable<'a, 'b, usize, &'b mut dyn Iterator<Item=usize>>
for Box<dyn B + 'a> where 'a: 'b, B : Bits<'a, usize> + Biterable<'a, 'b, usize, Ones<'b>> {
in a vain search for something that compiles and works (this fails because while Bits and Biterable are traits, evidently Biterable + Bits is not a trait). Seriously - a stateless, no-allocation-needed wrapper for one call on this thing returning one call on that thing, just not exposing that thing's type to the caller. That's it. The Java equivalent would be Supplier<T> a = ...; return () -> a.get();
I have to be thinking about this problem wrong. How?
It does certainly seem like you're over-complicating things. You have a lot of lifetime annotations that don't seem necessary. Here's a straightforward implementation (ignoring generic S):
use fixedbitset::FixedBitSet; // 0.2.0
pub trait Bits {
fn size(&self) -> usize;
fn contains(&self, s: usize) -> bool;
fn set_bits<'a>(&'a mut self) -> Box<dyn Iterator<Item = usize> + 'a>;
}
impl Bits for FixedBitSet {
fn size(&self) -> usize {
self.len()
}
fn contains(&self, s: usize) -> bool {
self.contains(s)
}
fn set_bits<'a>(&'a mut self) -> Box<dyn Iterator<Item = usize> + 'a> {
Box::new(self.ones())
}
}
pub fn get_bits_from_api() -> impl Bits {
FixedBitSet::with_capacity(64)
}
If you want the index type to be anonymous as well, make it an associated type and not define it when exposing your Bits:
use fixedbitset::FixedBitSet; // 0.2.0
pub trait Bits {
type Idx: std::ops::Add<Self::Idx>;
fn size(&self) -> Self::Idx;
fn contains(&self, s: Self::Idx) -> bool;
fn set_bits<'a>(&'a self) -> Box<dyn Iterator<Item = Self::Idx> + 'a>;
}
impl Bits for FixedBitSet {
type Idx = usize;
fn size(&self) -> Self::Idx {
self.len()
}
fn contains(&self, s: Self::Idx) -> bool {
self.contains(s)
}
fn set_bits<'a>(&'a self) -> Box<dyn Iterator<Item = Self::Idx> + 'a> {
Box::new(self.ones())
}
}
pub fn get_bits_from_api() -> impl Bits {
// ^^^^^^^^^ doesn't have <Idx = usize>
FixedBitSet::with_capacity(64)
}
fn main() {
let bits = get_bits_from_api();
// just a demonstration that it compiles
let size = bits.size();
if bits.contains(size) {
for indexes in bits.set_bits() {
// ...
}
}
}
I highly encourage against this though for many reasons. 1) You'd need many more constraints than just Add for this to be remotely usable. 2) You are severely limited with impl Bits; its not fully defined so you can't have dyn Bits or store it in a struct. 3) I don't see much benefit in being generic in this regard.

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

Lifetimes for generalizing trait implementation from &T to AsRef<T>

I have some problems generalizing a trait working for &str to other string types (e.g. Rc<str>, Box<str>,String).
First of all my example function should work for:
assert_eq!(count("ababbc", 'a'), 2); // already working
assert_eq!(count(Rc::from("ababbc"), 'a'), 2); // todo
assert_eq!(count("ababbc".to_string(), 'a'), 2); // todo
This is the working code, which makes the first test run:
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
pub trait Atoms<A, I>
where
I: Iterator<Item = A>,
A: Atom,
{
fn atoms(&self) -> I;
}
impl<'a> Atoms<char, std::str::Chars<'a>> for &'a str {
fn atoms(&self) -> std::str::Chars<'a> {
self.chars()
}
}
pub fn count<I, A, T>(pattern: T, item: A) -> usize
where
A: Atom,
I: Iterator<Item = A>,
T: Atoms<A, I>,
{
pattern.atoms().filter(|i| *i == item).count()
}
To make the next tests run, I changed the signature of count and Atoms in following way:
pub trait Atoms<'a, A, I>
where
I: Iterator<Item = A> + 'a,
A: Atom,
{
fn atoms<'b>(&'b self) -> I
where
'b: 'a;
}
impl<'a, S> Atoms<'a, char, std::str::Chars<'a>> for S
where
S: AsRef<str> + 'a,
{
fn atoms<'b>(&'b self) -> std::str::Chars<'b>
where
'b: 'a,
{
self.as_ref().chars()
}
}
but now the function count does not compile any more:
pub fn count<'a, I, A, T>(pattern: T, item: A) -> usize
where
A: Atom,
I: Iterator<Item = A> + 'a,
T: Atoms<'a, A, I>,
{
pattern.atoms().filter(|i| *i == item).count()
}
Playground-Link
The compiler error is: the parameter type 'T' may not live long enough ... consider adding an explicit lifetime bound...: 'T: 'a'. This is not completely understandable for me, so I tried to apply the help T: Atoms<'a, A, I> + 'a. Now the error is: 'pattern' does not live long enough ... 'pattern' dropped here while still borrowed.
Since the latter error also occurs without implementations of Atoms and by just replacing the function body by pattern.atoms();return 1; I suspect that the type signature of Atoms is not suitable for my purpose.
Has anybody a hint what could be wrong with the type signature of Atoms or count?
trait Atoms<'a, A, I> where I: Iterator<Item = A> + 'a ...
By writing this, you're requiring the iterator to outlive the lifetime 'a. Since 'a is part of the trait's generic parameters, it must be a lifetime that extends before you start using (implicitly) <String as Atoms<'a, char, std::str::Chars>>::atoms. This directly contradicts the idea of returning a new iterator object, since that object did not exist before the call to atoms().
Unfortunately, I'm not sure how to rewrite this so that it will work, without generic associated types (a feature not yet stabilized), but I hope that at least this explanation of the problem helps.
I hope I clarify at least some parts of the problem. After doing some internet research on HRTBs and GATs I come to the conclusion that my problem is hardly solvable with stable rust.
The main problem is that one cannot
have a trait with different lifetime signature than its implementations
keep lifetimes generic in a trait for later instantiation in the implementation
limit the upper bound of a results lifetime if it is owned
I tried several approaches to but most evolve to fail:
at compiling the implementation (because the implementations lifetimes conflict with those of the trait)
at compiling the caller of the trait because a compiling implementation limits the lifetimes in a way, that no object can satisfy them.
At last I found two solutions:
Implement the trait for references
the function atoms(self) does now expect Self and not &Self
Atoms<A,I> is implemented for &'a str and &'a S where S:AsRef<str>
This gives us control of the lifetimes of the self objects ('a) and strips the lifetime completely from the trait.
The disadvantage of this approach is that we have to pass references to our count function even for smart references.
Playground-Link
use std::fmt::Display;
use std::fmt::Debug;
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
pub trait Atoms<A, I>
where
I: Iterator<Item = A>,
A: Atom,
{
fn atoms(self) -> I;
}
impl<'a> Atoms<char, std::str::Chars<'a>> for &'a str {
fn atoms(self) -> std::str::Chars<'a> {
self.chars()
}
}
impl<'a, S> Atoms<char, std::str::Chars<'a>> for &'a S
where
S: AsRef<str>,
{
fn atoms(self) -> std::str::Chars<'a> {
self.as_ref().chars()
}
}
pub fn count<I, A, T>(pattern: T, item: A) -> usize
where
A: Atom,
I: Iterator<Item = A>,
T: Atoms<A, I>,
{
pattern.atoms().filter(|i| *i == item).count()
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use super::*;
#[test]
fn test_example() {
assert_eq!(count("ababbc", 'a'), 2);
assert_eq!(count(&"ababbc".to_string(), 'a'), 2);
assert_eq!(count(&Rc::from("ababbc"), 'a'), 2);
}
}
Switch to Generic Associated Types (unstable)
reduce the generic type Atoms<A,I> to an type Atoms<A> with an generic associated type I<'a> (which is instantiable at implementations)
now the function count can refer to the lifetime of I like this fn atoms<'a>(&'a self) -> Self::I<'a>
and all implementations just have to define how the want to map the lifetime 'a to their own lifetime (for example to Chars<'a>)
In this case we have all lifetime constraints in the trait, the implementation can consider to map this lifetime or ignore it. The trait, the implementation and the call site are concise and do not require references or helper lifetimes.
The disadvantage of this solution is that it is unstable, I do not know whether this means that runtime failures would probably occur or that api could change (or both). You will have to activate #![feature(generic_associated_types)] to let it run.
Playground-Link
use std::{fmt::Display, str::Chars};
use std::{fmt::Debug, rc::Rc};
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
pub trait Atoms<A>
where
A: Atom,
{
type I<'a>: Iterator<Item = A>;
fn atoms<'a>(&'a self) -> Self::I<'a>;
}
impl Atoms<char> for str
{
type I<'a> = Chars<'a>;
fn atoms<'a>(&'a self) -> Chars<'a> {
self.chars()
}
}
impl <S> Atoms<char> for S
where
S: AsRef<str>,
{
type I<'a> = Chars<'a>;
fn atoms<'a>(&'a self) -> Chars<'a> {
self.as_ref().chars()
}
}
pub fn count<A, S>(pattern: S, item: A) -> usize
where
A: Atom,
S: Atoms<A>,
{
pattern.atoms().filter(|i| *i == item).count()
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use super::*;
#[test]
fn test_example() {
assert_eq!(count("ababbc", 'a'), 2);
assert_eq!(count("ababbc".to_string(), 'a'), 2);
assert_eq!(count(Rc::from("ababbc"), 'a'), 2);
}
}

How do I specify lifetime parameters in an associated type?

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

Resources