References preferable to pass to function?

Hi all,

I'd like to decide how to pass some values to a function. This function's signature looks like

fn foo(&self, data: &[String]) -> u64

The function is implemented by some struct that does own the underlying Strings stored in a Vec<String>. The passed data is just some slice and I'd like to figure out how to pass that data to the function foo regarding performance.

My understanding of what is happening behind the scene when foo gets invoked says: Calling foo puts a reference to an array of Strings on the stack but leaves the Strings themself untouched (uncloned) on the heap. Regarding the costs a call to foo creates a single pointer on the stack but does nothing else and that would be best way to go.

Am I right?

I think I would agree with your thoughts.

Passing a slice is actually passing a fat pointer, that is the address and the length.

Note that passing a string by value would move the string, that is a struct with length, capacity, and address of the actual (heap) data is passed. Only when you call .clone() explicitly a copy of the actual heap data would occur. For such a possible move obviously the caller could not any longer use the string as ownership is passed. Note, that general not only the costs of passing a parameter to a function is important. The function generally has to access the data. When data is passed by reference, this is an indirection. For example, passing an i64 as value should be more efficient then passing a reference to it. But the compiler might optimize it.

1 Like

Below is an example for the case that you would not pass the data as slice (reference): In the first case, a move would occur, you could not any longer access the data in main. And in the second case, the clone would do a deep copy which includes copying the heap data of the strings.

fn main() {
    let words = vec![
        String::from("Hello"),
        String::from("Rust"),
        String::from("World"),
    ];

    print_strings(words);
    //print_strings(words); // value used here after move
    
    // print_strings(words.clone()); // deep copy -- string heap data is copied
    // print_strings(words);
}

fn print_strings(strings: Vec<String>) {
    for (i, s) in strings.iter().enumerate() {
        println!("String {}: {}", i + 1, s);
    }
}
1 Like