2016-09-21 11 views
2

Я начал с функцией немного, как это (playground):Как я могу обобщить функцию, которая принимает `& [T]`, так что я все еще могу вызвать ее с помощью литерала bytestring?

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));  
} 

И я хотел бы обобщить так, что я могу использовать любой подходящий тип для src. Лучшее, что я придумал это (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)); 
} 

Однако, сейчас я не могу просто вызвать find(x, 1), я должен создать срез: find(&x[..], 1).

Есть ли способ сделать этот общий, но все же можно вызвать find, как в исходном примере?

+0

Похоже, что в первом примере компилятор ржавчины совершает некоторые хитрости за кулисами и позволяет '&[u8; 3]' быть принужденными к '& [u8]', но не может сделать это во втором примере, потому что он не может точно знаю, что это намерение. Это в основном проблема? –

+0

Я попытался абстрагироваться от Index и некоторых других вещей, но я не убежден, что вам нужно вводить такие абстракции вообще - они, как правило, усложняют вещи без добавленной стоимости. К каким другим типам 'src'' find() 'может относиться? – ljedrz

+1

Хитрость такова: принуждение срабатывает, если известен тип назначения. Поскольку 'find' использует параметр типа для' src', это не так. – bluss

ответ

2

Ваш второй пример в настоящее время не работает из-за ограничения в компиляторе ржавчины (https://github.com/rust-lang/rust/issues/29504). Однако есть несколько способов обойти это.

Самый простой способ - реализовать RandomAccess<T> для всех C: AsRef<[T]>. Таким образом, он будет работать с [T; n], &[T], Vec<T> и т.д .:

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

К сожалению, вы не сможете добавить любые другие RandomAccess impls если вы сделаете это, так что вы можете также просто изменить find принять некоторые коллекции, удовлетворяющие AsRef<[T]>:

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

с другой стороны, если вам нужно, чтобы быть в состоянии поддерживать коллекции, не могут быть заимствованы, как [T], вы можете реализовать RandomAccess<T> для [T; n] для всех n в некотором диапазоне, используя макрос:

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

Когда мы в конечном итоге получить константы типа уровня (в настоящее время на список пожеланий), вы должны иметь возможность просто реализовать RandomAccess<T> для всех [T; n]. Но на данный момент вам нужно будет использовать макрос.

+0

'AsRef' имеет такое же ограничение, которое реализовано только до' [T; 32] '. – delnan

+1

Можете ли вы объяснить механизм, с помощью которого работает более простая версия ('fn find (src: & [T])')? Является ли особый случай компилятора для принуждения '&[T; n]' to '& [T]'? –

+1

Это ошибка: https://github.com/rust-lang/rust/issues/29504. Я обновил ответ. – Steven