Replacing element of vector

Hello,

I am trying to replace an element in a vector with a new element which can only be constructed after getting ownership of the old element. An example could look like this:

#[derive(Debug)]
struct Foo(Box<i32>);

#[derive(Debug)]
enum FooOrInt {
    Foo(Foo),
    Int(i32)
}

fn make_int(vec: &mut Vec<FooOrInt>, index: usize) {
    vec[index] = FooOrInt::Int(match vec[index] {
        FooOrInt::Foo(foo) => *foo.0,
        _ => panic!()
    });
}

fn main() {
    let mut value = vec![FooOrInt::Foo(Foo(Box::new(1))), FooOrInt::Foo(Foo(Box::new(2)))];
    println!("{:?}", value);
    make_int(&mut value, 1);
    println!("{:?}", value);
}

Unfortunately I do not know how this can be achieved. What is a good solution to this problem?

How about this?

fn make_int(vec: &mut Vec<FooOrInt>, index: usize) {
    // take ownership, putting in a dummy value
    let value = std::mem::replace(&mut vec[index], FooOrInt::Int(0));
    
    // compute new value
    let new_value = match value {
        FooOrInt::Foo(foo) => *foo.0,
        _ => panic!()
    };

    // put the new value back in
    vec[index] = FooOrInt::Int(new_value);
}
1 Like

Thanks for the answer! Unfortunately, for my use case, a dummy value could be quite expensive. Is there a way to avoid that?
Or could maybe an unsafe option work which just writes zeros?

What is the type that is expensive to create a dummy value for? The reason you need the dummy value is that the compiler needs an answer the question "what happens if you panic after taking ownership, but before putting in a new value?".

One option you can go for is the take_mut crate, which answers that question by aborting the process in case of panics.

5 Likes

Another option would be to .swap_remove() the old element out and .push() and .swap() the new element back. With this approach the elements can unexpectedly be reordered on panic.

6 Likes

Here's an article about approaches to this situation ("the sentinel pattern").

1 Like