Impl generic over slices and vectors


#1

I have struct Wrapper<T>(T) where T is expected to be [U], Vec<U>, or ideally anything that can be seen as a slice (since I want to call .chunks() on it).

How can I write an impl that accepts anything slice-like?

impl<T, U> Wrapper<T> where T: AsRef<[U]> {}

is not accepted, since U is unconstrained (which it really is, I totally don’t care about that type).


#2

EDIT: Ignore this post, somehow I ended up in some random crate’s documentation instead of the rust docs. AsSlice doesn’t exist :frowning:

You’re looking for:

_ impl <T: AsSlice> Wrapper{_
_ fn do_work(&self){_
_ var slice = self.as_slice();_
_ }_
_ }_

For completeness - this will allow you to accept Vec, [T; N], and references to both of those types


#3

That’s tough. It works with AsSlice, but then I can’t name the type in the slice, which severely limits what I can do with it. With AsSlice<U> I’m back to square one.


#4

Only way I can see is to use PhantomData to pretend you use U:

use std::marker::PhantomData;

struct Wrapper<T, U>(T, PhantomData<U>);

impl<T, U> Wrapper<T, U> where T: AsRef<[U]> {
    fn foo(&self) {
        for chunk in self.0.as_ref().chunks(5) {
        }
    }
}

EDIT: Actually, second way to do it, use a custom trait with an output type since it doesn’t make sense to be able to view a single type as slices of different types, then you can use the associated type to refer to the item type:

trait MyAsSlice {
    type Item;
    fn as_slice(&self) -> &[Self::Item];
}

impl<'a, T> MyAsSlice for &'a [T] {
    type Item = T;
    fn as_slice(&self) -> &[Self::Item] { self }
}

impl<'a, T> MyAsSlice for Vec<T> {
    type Item = T;
    fn as_slice(&self) -> &[Self::Item] { &self }
}

struct Wrapper<T>(T);

impl<T> Wrapper<T> where T: MyAsSlice {
    fn foo(&self) {
        let slice: &[T::Item] = self.0.as_slice();
        for chunk in slice.chunks(5) {
        }
    }
}

(maybe this is what @burns47 was referring to with AsSlice, that’s not in std and I don’t know what crates might provide it for you though).


#5

It should be possible by bounding on Deref:

impl<T, U> Wrapper<T> where T: Deref<Target=[U]> {...}

#6

Deref works for Vec and slices, although unfortunately arrays and references to arrays don’t implement Deref (not sure whether this is an issue in the original use case since you can explicitly convert to a slice, and potentially this could be changed with const generics).

I’m actually confused why this doesn’t hit something like the unconstrained type parameter issues that AsRef does, even though you’re binding U to Deref::Target you’re doing that via an indirection through the slice type, it seems like that indirection should provoke some similar sort of type resolution issues.

Thinking about it slightly more I see why this works. The slice type is a one-to-one type constructor, and Deref::Target is guaranteed to resolve to a single type for each T, so by first checking that <T as Deref>::Target == [?] then setting U == ? you’re guaranteed to get a single type of U for each Wrapper<T>.

The problem with AsRef doesn’t actually appear with any std types, but as an example imagine something like:

struct U32OrU8(&[u32], &[u8]);

impl AsRef<[u32]> for U32OrU8 {
    fn as_ref(&self) -> &[u32] {
        self.0
    }
}

impl AsRef<[u8]> for U32OrU8 {
    fn as_ref(&self) -> &[u8] {
        self.1
    }
}

For Wrapper<U32OrU8> U could be either u32 or u8 and you have no way to decide.

I wonder if there’s some middle ground between AsRef and Deref that you want here, something that uses an associated type like Deref so that you know there’s a single implementation, but doesn’t trigger Deref coercions as people seem to be a bit wary of those.

(Sorry about the rambling, I hope there’s at least some value in this post to offset it).


#7

There wouldn’t be any deref coercion here - Deref is just a bound and there would be an explicit deref() inside the method.


#8

Personally, I would probably go with adding PhantomData, as @Nemo157 suggested. After all, this is exactly the use case described in its docs.