5

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
tnibbles
  • 93
  • 5
  • Idiomatic Rust does not use the `return` keyword at the end of a block. Instead, you leave off the `return` and the semicolon. – Shepmaster Sep 11 '18 at 01:19
  • 1
    "is a reference returned or is the entire vector copied?", that depend of what you mean by the entire vector, your function create a vec and give the ownership to the caller, it's a move, don't worry this should be fine, without any problem of performance. – Stargateur Sep 11 '18 at 01:22
  • Question updated – tnibbles Sep 11 '18 at 01:29

1 Answers1

9

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thank you for the explanation. I can make sense of the linked question with this extra information. – tnibbles Sep 11 '18 at 01:54