Associated types that are references


#1

Consider a simple trait like this one

trait HasItem {
    type Item
    fn item(&self) -> Self::Item;
}

What I want is that implementers of this trait can choose Self::Item to be an owned return value or a reference to some value stored in the struct implementing the trait, something like this:

struct ValAccess { it: u32 }
struct RefAccess { it: u32 }

impl HasItem for ValAccess {
    type Item = u32;
    fn item(&self) -> Self::Item { self.it }
}

impl HasItem for RefAccess {
    type Item = &u32;
    fn item(&self) -> Self::Item { &self.it }
}

The implementation of HasItem for ValAccess is perfectly sound, but the compiler (obviously) complains about a missing lifetime for the Item type in RefAccess's implementation of HasItem.

I can fix this by adding a lifetime bound to the trait itself:

trait HasItem<'a> {
    type Item
    fn item(&'a self) -> Self::Item;
}

struct ValAccess { it: u32 }
struct RefAccess { it: u32 }

impl<'a> HasItem<'a> for ValAccess {
    type Item = u32;
    fn item(&'a self) -> Self::Item { self.it }
}

impl<'a> HasItem<'a> for RefAccess {
    type Item = &'a u32;
    fn item(&'a self) -> &'a u32 { &self.it }
}

But this seems awkward for two reasons: (1) The module defining the trait needs to anticipate that in some implementations the Item may be a reference. (2) The lifetime parameter now needs to be specified everywhere the trait is used. I guess what I really want is something like this:

trait HasItem {
    type Item
    fn item(&self) -> Self::Item;
}

struct ValAccess { it: u32 }
struct RefAccess { it: u32 }

impl HasItem for ValAccess {
    type Item = u32;
    fn item(&self) -> Self::Item { self.it }
}

impl<'a> HasItem for RefAccess
    where Self: 'a // <-- The idea is to put a lifetime bound on Self
                   //     so the lifetime bound of Item is tied to Self's
                   //     lifetime
{
    type Item = &'a u32;
    fn item(&self) -> Self::Item { &self.it }
}

Is anything of this sort possible at the moment? It seems to me that choosing between returning owned values or references is something rather natural to do, so I hope it’s possible to do this without putting additional warts on the trait itself.


#2

Coincidentally, there’s a similar thread today, https://users.rust-lang.org/t/inheriting-trait-implementations-or-higher-ranked-trait-bound-flexibility, that is essentially the same question as yours :slight_smile:

The long term solution will be generic associated types (GAT), but that’s likely 6-12 months out.


#3

Thanks, Vitaly. Great that this is in the works. 6-12 months is not ideal, but that’s what you get when you use a language that’s still heavily in development. In the meantime, I’ll work around it by just always returning references to the item type, in which case the item type itself does not need to be a reference.


#4

Just to be clear: 6-12 months is a guesstimate on my part. Could be less (doubtful given Rust’s 2018 roadmap) or could be more :slight_smile:. But the main idea is it’s a known issue with a solution in the pipeline.


#5

Understood. I’ll be patient.