Trait implementation for function with generics that takes slice of associated type as an argument

This seems kind of a simple idea, but i can't get my head around it.

I need to define a trait, that requires that some generic function accepts slice of a defined type.

trait SomeTrait {
    type Base;
    fn some_fun<T>(&self, val: T)
}

What i would like to achieve is that when i implement the trait on String type it would essentially become:

impl SomeTrait for MyStruct {
    type Base = String;
    fn some_fun<T: &str>(&self, val: &str)
}

If this trait was implemented for Base = Vec, then T should be &[u8], etc. What i need is that in specific structs implementation i can use passed in variable with that type. I.e. if struct is String, then it should be able to get &str out of T and use that.

The closest way to achieve what i have described that i found was to define reference lifetime at whole trait generics level, but it constraints lifetime for whole struct when it should not, because when we call some_fun the lifetime for input &str should be only dependent on calling context and be independent from MyStruct lifetime.

All helpful ideas are welcome.

Thanks.

you need the Deref trait as a bound for the associated type, something like:

trait SomeTrait {
    type Base: Deref;
    fn some_fun(&self, val: &<Self::Base as Deref>::Target);
}
2 Likes

Alternative to Deref would be ToOwned:

trait SomeTrait {
    type Base;
    fn some_fun<T: ToOwned<Owned = Self::Base> + ?Sized>(&self, val: &T);
}

impl SomeTrait for String {
    type Base = Self;

    fn some_fun<T: ToOwned<Owned = Self::Base> + ?Sized>(&self, val: &T) {}
}

impl SomeTrait for Vec<u8> {
    type Base = Self;

    fn some_fun<T: ToOwned<Owned = Self::Base> + ?Sized>(&self, val: &T) {}
}

fn main() {
    let x = "foo".to_owned();
    x.some_fun("foo");

    let x = vec![0, 1, 2];
    x.some_fun([0, 1, 2].as_slice());
}

Playground.

Here a playground with Deref.

2 Likes

Both are great answers and they are applicable to my use-case. I set ToOwned as solution, because in my use-case it generalizes a bit better, though i could require Clone and achieve similar results with Deref approach.

Thanks again for both. You saved my day!

neat! and for the String/&str and Vec<T>/&[T], Borrow may also be used.

trait SomeTrait {
    type Base;
    fn some_fun<T: ?Sized>(&self, val: &T)
    where
        Self::Base: Borrow<T>;
}
3 Likes

Borrow is even nicer than ToOwned, because of the ?Sized bound on Borrowed. I just generally steer away from that trait given the unfortunate overlap of Borrow::borrow with RefCell::borrow.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.