4

In C you can use the pointer offset to get index of an element within an array, e.g.:

index = element_pointer - &vector[0];

Given a reference to an element in an array, this should be possible in Rust too.

While Rust has the ability to get the memory address from vector elements, convert them to usize, then subtract them - is there a more convenient/idiomatic way to do this in Rust?

1

2 Answers 2

5

There isn't a simpler way. I think the reason is that it would be hard to guarantee that any operation or method that gave you that answer only allowed you to use it with the a Vec (or more likely slice) and something inside that collection; Rust wouldn't want to allow you to call it with a reference into a different vector.

More idiomatic would be to avoid needing to do it in the first place. You can't store references into Vec anywhere very permanent away from the Vec anyway due to lifetimes, so you're likely to have the index handy when you've got the reference anyway.

In particular, when iterating, for example, you'd use the enumerate to iterate over pairs (index, &item).

Sign up to request clarification or add additional context in comments.

1 Comment

Enumerate is fine for the common case, however there are times where a function may (in a special case) - need to get the parent, then remove a reference from a vector. Where passing an index along is possible but inconvenient.
2

So, given the issues which people have brought up with the pointers and stuff; the best way, imho, to do this is:

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

although, if you want methods:

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> {
        // ...
    }
}

and then you'll be able to use this method for any type that Derefs to [T]

1 Comment

I would generally recommend a debug_assert in index_of_unchecked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.