考虑这个函数,它从具有相同生命周期的两个切片中返回最长的字符串切片。
fn get_longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
现在考虑这个测试函数,它应该失败,因为参数具有不同的生命周期。
#[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");
}
这是测试函数正确产生的错误。
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());
现在假设我无权访问测试函数,因此无法像编译器建议的那样更改它,因此为了使其通过,我将
get_longest
的返回类型更改为 String
。
fn get_longest(s1: &str, s2: &str) -> String {
if s1.len() > s2.len() {
s1.to_owned()
} else {
s2.to_owned()
}
}
如何更改原始函数
get_longest
,以便测试函数with_different_lifetimes
通过,并且仍然返回对字符串切片的引用而不是新的String
?
换句话说,这个比较引用的测试函数也应该通过。
#[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);
}
这可能吗?
第二次测试的唯一问题是您无法比较
String
和 &String
。比较运算符通常不在拥有的类型和引用之间实现,因为运算符已经通过引用获取其操作数。不过,这样做也没有什么坏处。
您无法制作
impl PartialEq<&String> for String
,但您可以制作与 &str
和 &String
相当的包装类型。这将使两个测试都编译并通过。
#[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())
}
}