Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Under the hood, are vectors in Rust returned by reference or value?

Tags:

memory

rust

I am trying to learn the ins and outs of memory in Rust. When a vector is created inside a function and then returned, is a reference returned or is the entire vector copied?

Example:

use std::io;

fn line_to_ints() -> Vec<u32> {
    let mut line = String::new();

    io::stdin()
        .read_line(&mut line)
        .expect("Failed to read line");

    return line
        .split(" ")
        .map(|x| x.parse().expect("Not an integer!"))
        .collect();
}

Will the return behavior here also be the same for all other non-primitive data types?

Unlike Is there any way to return a reference to a variable created in a function?, I would like to know a bit more about what is happening under the hood. The answers to that question do not provide clarity as to whether or not the vector is created and then copied to a new location, or ownership of the pointer is returned I understand vectors are created on the heap so I imagine a pointer is involved.

like image 401
tnibbles Avatar asked Jan 28 '23 11:01

tnibbles


1 Answers

is a reference returned

No. It cannot be because there's nothing to reference once the function ends. This is covered in detail in Is there any way to return a reference to a variable created in a function?.

is the entire vector copied

Yes, but probably not how you mean it. A Vec is basically defined as

struct Vec<T> {
    capacity: usize,
    length: usize,
    data: *mut T,
}

Semantically, these 3 pointer-sized fields are moved from the function to the caller. The N elements contained by the vector are not copied.

Implementation-wise, the compiler/optimizer can pick from a large bag of tricks:

  • Actually copy all three fields
  • Pass in a secret mutable reference and have the function write directly to it
  • Inline the function where it's called
  • Perform dead-code removal and never call the function in the first place
  • Probably others...

The only way to know which it picks is to look at the MIR / LLVM IR / assembly.

Will the return behavior here also be the same for all other non-primitive data types?

Yes. Rust's data types are all treated the same. Primitive vs. non-primitive means nothing for the semantics of the language.

See also:

  • Can I efficiently return an object by value in Rust?
  • Inefficient instance construction?
  • What the difference is between using the box keyword and Box::new?
  • What are move semantics in Rust?
  • How does Rust provide move semantics?
like image 181
Shepmaster Avatar answered Jan 30 '23 01:01

Shepmaster