Reputation: 702
I'm walking through Microsoft's Rust tutorial here, which is about
implement the
copy_and_return
function so that it returns a reference to the value inserted in the vector
Solution is given here, but it's different from mine in that it used &String as return type while I used &str.
// standard solution
fn copy_and_return<'a>(vector: &'a mut Vec<String>, value: &'a str) -> &'a String {
vector.push(String::from(value));
vector.get(vector.len() - 1).unwrap()
}
// my solution
fn copy_and_return<'a>(vector: &'a mut Vec<String>, value: &'a str) -> &'a str {
vector.push(String::from(value));
return value; // simply return value
}
fn main() {
let name1 = "Joe";
let name2 = "Chris";
let name3 = "Anne";
let mut names = Vec::new();
assert_eq!("Joe", copy_and_return(&mut names, &name1));
assert_eq!("Chris", copy_and_return(&mut names, &name2));
assert_eq!("Anne", copy_and_return(&mut names, &name3));
assert_eq!(
names,
vec!["Joe".to_string(), "Chris".to_string(), "Anne".to_string()]
)
}
In addition to the return type, another difference between mine and the standard solution is that I simply returned the parameter value
, while the standard solution used complicated way vector.get(vector.len() - 1).unwrap()
.
I'm wondering if there's anything wrong with my solution that the tutorial takes another way?
While @Masklinn provided a great answer to my question, it's a bit specific to the example I gave but not directly addressing the title What is the difference between &str and &String
.
I found this discussion to be quite informative:
Basically a
String
wraps and manages a dynamically allocated str as backing storage. Since str cannot be resized, String will dynamically allocate/deallocate memory. A&str
is thus a reference directly into the backing storage of the String, while &String is a reference to the “wrapper” object. Additionaly, &str can be used for substrings, i.e. they are slices. A &String references always the whole string.
Chapter 4.3 of the Rust book also helps
Upvotes: 2
Views: 764
Reputation: 171
There really isn't much of a practical difference. You didn't do what they asked, but it doesn't matter. There's no reason why you would want to use Microsoft's solution instead of yours. Theirs is doing more work for no reason when you can just do what you did.
But it's a bit more complex than that. Having the same lifetime means Rust enforces those things all live as long as each other. If you have a lifetime parameter where you don't need to, then you are making there be an unnecessary restriction. In Microsoft's solution, the lifetime for the value parameter is unnecessary because you are returning something related to the vector and nothing to do with the &str. In certain cases it will prevent you from doing things for no reason. In your solution it's the exact opposite. You're returning the &str, but the vector has nothing to do with that. In certain cases it will also prevent you from doing things for no reason. You won't be able to access the returned &str if you have done anything mutable with the vec. Your function should look like this if you don't care about Microsoft's very stupid solution and just want it to be practical:
fn copy_and_return<'a>(vec: &mut Vec<String>, str: &'a str) -> &'a str {
vector.push(String::from(value));
value
}
Other improvements:
Upvotes: 0
Reputation: 42197
I'm wondering if there's anything wrong with my solution that the tutorial takes another way?
I don't think there's anything wrong per se, yours might even match the function name better depending how its interpreted: are you supposed to copy and return the original, or copy and return the copy? Yours is the first pick, theirs is the second.
It's made irrelevant by the lifetimes, but this does make a difference to program behaviour: in the "official" solution, the result is a reference to the value inserted into the Vec
, meaning it will be "live" for as long as the vector is (at least assuming the vector is not otherwise modified).
You can see the difference if you replace value: &'a str
by value: &'_ str
(aka "a lifetime you don't care about but is different from 'a
): the official solution still compiles, yours does not.
Though note that the official could just as well return &'a str
regardless:
fn copy_and_return<'a>(vector: &'a mut Vec<String>, value: &'_ str) -> &'a str {
vector.push(String::from(value));
vector.get(vector.len() - 1).unwrap()
}
The official solution is hardly great through e.g.
vector.get(vector.len() - 1)
is a complicated way of writing
vector.last()
but it might be that they just don't want to overwhelm the reader with advanced APIs or something, I could not say.
Upvotes: 7