patternrustCritical
Why is it discouraged to accept a reference &String, &Vec, or &Box as a function argument?
Viewed 0 times
boxacceptdiscouragedfunctionwhyargumentvecstringreference
Problem
I wrote some Rust code that takes a
I've also written code that takes in a reference to a
However, I received some feedback that doing it like this isn't a good idea. Why not?
&String as an argument: fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}I've also written code that takes in a reference to a
Vec or Box:fn total_price(prices: &Vec) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box) -> bool {
**value % 2 == 0
}However, I received some feedback that doing it like this isn't a good idea. Why not?
Solution
TL;DR: One can instead use
-
One of the main reasons to use a
-
Accepting a
-
Another performance consideration is that
Instead, you should accept a string slice (
Now you can call these methods with a broader set of types. For example,
If you'd like to add or remove items from the
Specifically for slices, you can also accept a
&str, &[T] or &T to allow for more generic code.-
One of the main reasons to use a
String or a Vec is because they allow increasing or decreasing the capacity. However, when you accept an immutable reference, you cannot use any of those interesting methods on the Vec or String.-
Accepting a
&String, &Vec or &Box also requires the argument to be allocated on the heap before you can call the function. Accepting a &str allows a string literal (saved in the program data) and accepting a &[T] or &T allows a stack-allocated array or variable. Unnecessary allocation is a performance loss. This is usually exposed right away when you try to call these methods in a test or a main method:awesome_greeting(&String::from("Anna"));total_price(&vec![42, 13, 1337])is_even(&Box::new(42))-
Another performance consideration is that
&String, &Vec and &Box introduce an unnecessary layer of indirection as you have to dereference the &String to get a String and then perform a second dereference to end up at &str.Instead, you should accept a string slice (
&str), a slice (&[T]), or just a reference (&T). A &String, &Vec or &Box will be automatically coerced (via deref coercion) to a &str, &[T] or &T, respectively.fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}fn is_even(value: &i32) -> bool {
*value % 2 == 0
}Now you can call these methods with a broader set of types. For example,
awesome_greeting can be called with a string literal ("Anna") or an allocated String. total_price can be called with a reference to an array (&[1, 2, 3]) or an allocated Vec.If you'd like to add or remove items from the
String or Vec, you can take a mutable reference (&mut String or &mut Vec):fn add_greeting_target(greeting: &mut String) {
greeting.push_str("world!");
}fn add_candy_prices(prices: &mut Vec) {
prices.push(5);
prices.push(25);
}Specifically for slices, you can also accept a
&mut [T] or &mut str. This allows you to mutate a specific value inside the slice, but you cannot change the number of items inside the slice (which means it's very restricted for strings):fn reset_first_price(prices: &mut [i32]) {
prices[0] = 0;
}fn lowercase_first_ascii_character(s: &mut str) {
if let Some(f) = s.get_mut(0..1) {
f.make_ascii_lowercase();
}
}Code Snippets
awesome_greeting(&String::from("Anna"));total_price(&vec![42, 13, 1337])is_even(&Box::new(42))fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}Context
Stack Overflow Q#40006219, score: 347
Revisions (0)
No revisions yet.