How can I generalise a function that accepts an `&[T]` so that I can still invoke it with a bytestring literal?

I started off with a function a bit like this (playground):

fn find <T: Copy> (src: &[T], index: usize) -> T {
    // more complex algorithm, involving src goes here
    src[index]
}

pub fn main() {
    let x = b"abc";
    assert_eq!(b'b', find(x, 1));    
}

And I wanted to generalise it so that I can use any appropriate type for src. The best I came up with is this (playground):

trait RandomAccess<T> {
    fn get_at(&self, index: usize) -> T;
}

impl <T: Copy> RandomAccess<T> for [T] {
    fn get_at(&self, index: usize) -> T {
        self[index]
    }
}

fn find <T: Copy, S: ?Sized + RandomAccess<T>> (src: &S, index: usize) -> T {
    // more complex algorithm, involving src goes here
    src.get_at(index)
}

pub fn main() {
    let x = b"xyz";
    assert_eq!(b'y', find(&x[..], 1));
}

However, I now can't just invoke find(x, 1), I have to create a slice: find(&x[..], 1).

Is there a way to make this generic, but still be able to invoke find as in the original example?

Answers


Your second example doesn't currently work due to a limitation in the rust compiler (https://github.com/rust-lang/rust/issues/29504). However, there are a few ways around this.

The simplest way is to implement RandomAccess<T> for all C: AsRef<[T]>. This way, it'll work with [T; n], &[T], Vec<T>, etc.:

trait RandomAccess<T> {
    fn get_at(&self, index: usize) -> T;
}

impl<T: Copy, C: AsRef<[T]>> RandomAccess<T> for C {
    fn get_at(&self, index: usize) -> T {
        self.as_ref()[index]
    }
}

fn find<T: Copy, C: RandomAccess<T>>(src: C, index: usize) -> T {
    src.get_at(index)
}

Unfortunately, you won't be able to add any other RandomAccess impls if you do that so you might as well just change find to take some collection satisfying AsRef<[T]>:

fn find<T: Copy, C: AsRef<[T]>> (src: C, index: usize) -> T {
    src.get_at(index)
}

Alternatively, if you need to be able to support collections that can't be borrowed as [T], you can implement RandomAccess<T> for [T; n] for all n in some range using a macro:

trait RandomAccess<T> {
    fn get_at(&self, index: usize) -> T;
}

impl<T: Copy> RandomAccess<T> for [T] {
    fn get_at(&self, index: usize) -> T {
        self[index]
    }
}

macro_rules! impl_random_access {
    ($($n:expr,)*) => {
        $(
            impl <T: Copy> RandomAccess<T> for [T; $n] {
                fn get_at(&self, index: usize) -> T {
                    self[index]
                }
            }
        )*
    }
}

impl_random_access! {
    01,02,03,04,05,06,07,08,
    09,10,11,12,13,14,15,16,
    17,18,19,20,21,22,23,24,
    25,26,27,28,29,30,31,32,
}



fn find<T: Copy, S: ?Sized + RandomAccess<T>>(src: &S, index: usize) -> T {
    src.get_at(index)
}

When we eventually get type-level constants (currently on the wish-list), you should be able to just implement RandomAccess<T> for all [T; n]. But for now, you'll need to use a macro.


Need Your Help

WPF Rectangle filled with horizontal or vertical lines

wpf drawing

I am trying to create one rectangle filled with Horizontal or Vertical lines.

Using <animate> on SVG viewBox itself only works twice

xml animation svg smil

I've been trying to use 2 different s on SVG viewBox to cycle between sizes, but though both animations work independently, the second one won't run if the first begins again after it. What's stran...