2016-08-30 12 views
1

В C вы можете использовать указатель смещения, чтобы получить индекс элемента в массиве, например .:Как получить индекс элемента в векторе, используя арифметику указателя?

index = element_pointer - &vector[0]; 

дана ссылка на элемент в массиве, это должно быть возможно в Русте тоже.

В то время как Rust имеет возможность получить адрес памяти из векторных элементов, преобразуйте их в usize, а затем вычтите их - есть ли более удобный/идиоматический способ сделать это в Rust?

+0

Это похоже на * [Как получить смещение байта между '& str'] (https://stackoverflow.com/questions/38268529/how-to-get-the-byte-offset-between-str) * где ответ был «просто конвертируется в' usize' ». – mcarton

ответ

1

Итак, учитывая проблемы, которые люди подняли с помощью указателей и прочее; лучший способ, имхо, чтобы сделать это:

fn index_of_unchecked<T>(slice: &[T], item: &T) -> usize { 
    if ::std::mem::size_of::<T>() == 0 { 
     return 0; // do what you will with this case 
    } 
    (item as *const _ as usize - slice.as_ptr() as usize) 
    /std::mem::size_of::<T>() 
} 

// note: for zero sized types here, you 
// return Some(0) if item as *const T == slice.as_ptr() 
// and None otherwise 
fn index_of<T>(slice: &[T], item: &T) -> Option<usize> { 
    let ptr = item as *const T; 
    if 
     slice.as_ptr() < ptr && 
     slice.as_ptr().offset(slice.len()) > ptr 
    { 
     Some(index_of_unchecked(slice, item)) 
    } else { 
     None 
    } 
} 

хотя, если вы хотите методы:

trait IndexOfExt<T> { 
    fn index_of_unchecked(&self, item: &T) -> usize; 
    fn index_of(&self, item: &T) -> Option<usize>; 
} 

impl<T> IndexOfExt<T> for [T] { 
    fn index_of_unchecked(&self, item: &T) -> usize { 
     // ... 
    } 
    fn index_of(&self, item: &T) -> Option<usize> { 
     // ... 
    } 
} 

, и тогда вы будете иметь возможность использовать этот метод для любого типа Deref с до [T]

+0

Я обычно рекомендую 'debug_assert' в' index_of_unchecked'. – ubsan

4

Нет более простого способа. Я думаю, причина в том, что было бы трудно гарантировать, что любая операция или метод, которые дали вам этот ответ, позволили вам использовать его только с Vec (или более вероятным фрагментом) и чем-то внутри этой коллекции; Руст не хотел бы позволять вам называть его ссылкой с другим вектором.

Более идиоматично было бы избежать необходимости делать это в первую очередь. Вы не можете хранить ссылки в Vec в любом месте, где-либо очень постоянном, от Vec в любом случае из-за времени жизни, поэтому у вас, вероятно, будет удобный указатель, если у вас есть ссылка.

В частности, при повторении, например, вы должны использовать enumerate для перебора пар (index, &item).

+0

Перечисление отлично подходит для обычного случая, однако есть моменты, когда функция может (в специальном случае) - получить родительский элемент, а затем удалить ссылку из вектора. Там, где прохождение индекса возможно, но неудобно. – ideasman42

Смежные вопросы