Search code examples
pointersrustreferencememcpyownership

Rust way to transfer ownership while guaranteeing no underlying data copy


I am a bit confused about how to transfer ownership without the overhead of actual data copy. I have the following code. I am referring to underlying data copy by OS as memcopy.

fn main() {
    let v1 = Vec::from([1; 1024]);
    take_ownership_but_memcopies(v1);

    let v2 = Vec::from([2; 1024]);
    dont_memecopy_but_dont_take_ownership(&v2);

    let v3 = Vec::from([3; 1024]);
    take_ownership_dont_memcopy(???);
}

// Moves but memcopies all elements
fn take_ownership_but_memcopies(my_vec1: Vec<i32>) {
    println!("{:?}", my_vec1);
}

// Doesn't memcopy but doesn't take ownership
fn dont_memecopy_but_dont_take_ownership(my_vec2: &Vec<i32>) {
    println!("{:?}", my_vec2);
}

// Take ownership without the overhead of memcopy
fn take_ownership_dont_memcopy(myvec3: ???) {
    println!("{:?}", my_vec3);
}

As i understand, if i use reference like v2, i don't get the ownership. If i use it like v1, there could be a memcopy.

How should i need to transfer v3 to guarantee that there is no underlying memcopy by OS?


Solution

  • Your understanding of what happens when you move a Vec is incorrect - it does not copy every element within the Vec!

    To understand why, we need to take a step back and look at how a Vec is represented internally:

    // This is slightly simplified, look at the source for more details!
    
    struct Vec<T> {
        pointer: *mut T, // pointer to the data (on the heap)
        capacity: usize, // the current capacity of the Vec
        len: usize,      // the current number of elements in the Vec
    }
    

    While the Vec conceptually 'owns' the elements, they are not stored within the Vec struct - it only holds a pointer to that data. So when you move a Vec, it is only the pointer (plus the capacity and length) that gets copied.


    If you are attempting to avoid copying altogether, as opposed to avoiding copying the contents of the Vec, that isn't really possible - in the semantics of the compiler, a move is a copy (just one that prevents you from using the old data afterwards). However, the compiler can and will optimize trivial copies into something more efficient.