Consider this function that returns the longest string slice from two slices with equal lifetimes.
fn get_longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
Now consider this test function that should fail because the arguments have different lifetimes.
#[test]
fn with_different_lifetimes() {
let s1 = "very long string";
let s2 = "tiny".to_string();
let should_be_s1 = get_longest(s1, &s2);
drop(s2);
assert_eq!(should_be_s1, "very long string");
}
This is the error that the test function correctly produces.
error[E0505]: cannot move out of `s2` because it is borrowed
let s2 = "tiny".to_string();
-- binding `s2` declared here
let should_be_s1 = get_longest(s1, &s2);
--- borrow of `s2` occurs here
drop(s2);
^^ move out of `s2` occurs here
assert_eq!(should_be_s1, "very long string");
-------------------------------------------- borrow later used here
help: consider cloning the value if the performance cost is acceptable
let should_be_s1 = get_longest(s1, &s2);
let should_be_s1 = get_longest(s1, s2.clone());
Now let's say I don't have access to the test function so I cannot change it like the compiler suggests, so to make it pass I changed the return type of get_longest
to String
.
fn get_longest(s1: &str, s2: &str) -> String {
if s1.len() > s2.len() {
s1.to_owned()
} else {
s2.to_owned()
}
}
How can I change the original function get_longest
so that the test function with_different_lifetimes
passes and it will still return a reference to a string slice instead of a new String
?
In other words, this test function that compares the references should also pass.
#[test]
fn equal_reference() {
let s1 = "tiny";
let s2 = "very long string".to_string();
let should_be_ref_s2 = get_longest(s1, &s2);
assert_eq!(should_be_ref_s2, &s2);
}
Is this possible?
The only issue with your second test is that you can't compare String
and &String
. The comparison operators are not often implemented between owned types and references since the operators take their operands by reference already. However, there's no harm in doing so.
You can't make impl PartialEq<&String> for String
, but you can make a wrapper type that is comparable to both &str
and &String
. This will make both tests compile and pass.
#[derive(Debug)]
pub struct StringCmp(pub String);
impl PartialEq<&String> for StringCmp {
fn eq(&self, other: &&String) -> bool {
self.0.eq(*other)
}
}
impl PartialEq<&str> for StringCmp {
fn eq(&self, other: &&str) -> bool {
self.0.eq(*other)
}
}
pub fn get_longest(s1: &str, s2: &str) -> StringCmp {
if s1.len() > s2.len() {
StringCmp(s1.to_owned())
} else {
StringCmp(s2.to_owned())
}
}