尝试在循环中更新 Option<&str> 时获取 "temporary value dropped while borrowed"
Getting "temporary value dropped while borrowed" when trying to update an Option<&str> in a loop
我正在尝试实现一种常用模式 - 在下一个循环迭代中使用上一个循环迭代的结果。比如实现分页,需要给出上一页最后一个值的id。
struct Result {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<&str> = None;
for i in 0..times {
let current = do_something(last);
last = match current {
Some(r) => Some(&r.str.to_owned()),
None => None,
};
}
}
fn do_something(o: Option<&str>) -> Option<Result> {
Some(Result {
str: "whatever string".to_string(),
})
}
但是,我不确定如何从循环中实际获取值。目前,编译器错误是temporary value dropped while borrowed
(在&r.str.to_owned()
),虽然我做了很多其他尝试,但无济于事。
我发现真正让它工作的唯一方法是创建某种本地 tmp_str
变量并像这样进行黑客攻击:
match current {
Some(r) => {
tmp_str.clone_from(&r.str);
last = Some(&tmp_str);
}
None => {
last = None;
}
}
但这并不像它应该做的那样。
r.str.to_owned()
是临时值。 ,但由于临时值通常会在最内层封闭语句的末尾被删除(销毁),因此引用在该点变为悬空。在这种情况下, "innermost enclosing statement" 要么是循环的最后一行,要么是循环体本身——我不确定究竟是哪一个适用于此,但这并不重要,因为无论哪种方式,你都是试图使 last
包含对即将被删除的 String
的引用,从而使 last
无法使用。编译器在循环的下一次迭代中阻止你再次使用它是正确的。
最简单的解决方法就是根本不使 last
成为引用——在示例中,这是不必要或不可取的。只需使用 Option<String>
:
fn main() {
let times = 10;
let mut last = None;
for _ in 0..times {
last = match do_something(last) {
Some(r) => Some(r.str),
None => None,
};
}
}
fn do_something(_: Option<String>) -> Option<Result> {
// ...
}
还有一些方法可以使参考版本正常工作;这是一个:
let mut current; // lift this declaration out of the loop so `current` will have
// a lifetime longer than one iteration
for _ in 0..times {
current = do_something(last);
last = match current {
Some(ref r) => Some(&r.str), // borrow from `current` in the loop instead
// of from a newly created String
None => None,
};
}
如果您的代码比示例更复杂并且使用 String
意味着很多潜在的昂贵 .clone()
s,您可能想要这样做。
在您的代码中,尚不清楚 last: Option<&str>
中引用的 String
的所有者应该是谁。您可以引入一个拥有该字符串的额外可变局部变量。但是你会有两个变量:所有者和引用,这似乎是多余的。让 last
成为所有者会简单得多:
struct MyRes {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<String> = None;
for _i in 0..times {
last = do_something(&last).map(|r| r.str);
}
}
fn do_something(_o: &Option<String>) -> Option<MyRes> {
Some(MyRes {
str: "whatever string".to_string(),
})
}
在do_something
中,您可以通过引用传递整个参数,这似乎更符合您的要求。另请注意,命名您自己的结构 Result
不是一个好主意,因为 Result
是编译器(?
-运算符等)中内置的如此普遍的特征。
后续问题:Option<&str>
或Option<String>
?
Option<&str>
和 Option<String>
都有不同的取舍。一种更适合传递字符串文字,另一种更适合传递拥有的 String
s。实际上,我建议两者都不使用,而是使函数泛型覆盖实现 AsRef<str>
的类型 S
。下面是各种方法的比较:
fn do_something(o: &Option<String>) {
let _a: Option<&str> = o.as_ref().map(|r| &**r);
let _b: Option<String> = o.clone();
}
fn do_something2(o: &Option<&str>) {
let _a: Option<&str> = o.clone(); // do you need it?
let _b: Option<String> = o.map(|r| r.to_string());
}
fn do_something3<S: AsRef<str>>(o: &Option<S>) {
let _a: Option<&str> = o.as_ref().map(|s| s.as_ref());
let _b: Option<String> = o.as_ref().map(|r| r.as_ref().to_string());
}
fn main() {
let x: Option<String> = None;
let y: Option<&str> = None;
do_something(&x); // nice
do_something(&y.map(|r| r.to_string())); // awkward & expensive
do_something2(&x.as_ref().map(|x| &**x)); // cheap but awkward
do_something2(&y); // nice
do_something3(&x); // nice
do_something3(&y); // nice, in both cases
}
请注意,并非所有上述组合都是非常惯用的,添加一些只是为了完整性(例如,要求 AsRef<str>
然后构建一个拥有的 String
似乎有点奇怪)。
我正在尝试实现一种常用模式 - 在下一个循环迭代中使用上一个循环迭代的结果。比如实现分页,需要给出上一页最后一个值的id。
struct Result {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<&str> = None;
for i in 0..times {
let current = do_something(last);
last = match current {
Some(r) => Some(&r.str.to_owned()),
None => None,
};
}
}
fn do_something(o: Option<&str>) -> Option<Result> {
Some(Result {
str: "whatever string".to_string(),
})
}
但是,我不确定如何从循环中实际获取值。目前,编译器错误是temporary value dropped while borrowed
(在&r.str.to_owned()
),虽然我做了很多其他尝试,但无济于事。
我发现真正让它工作的唯一方法是创建某种本地 tmp_str
变量并像这样进行黑客攻击:
match current {
Some(r) => {
tmp_str.clone_from(&r.str);
last = Some(&tmp_str);
}
None => {
last = None;
}
}
但这并不像它应该做的那样。
r.str.to_owned()
是临时值。 last
包含对即将被删除的 String
的引用,从而使 last
无法使用。编译器在循环的下一次迭代中阻止你再次使用它是正确的。
最简单的解决方法就是根本不使 last
成为引用——在示例中,这是不必要或不可取的。只需使用 Option<String>
:
fn main() {
let times = 10;
let mut last = None;
for _ in 0..times {
last = match do_something(last) {
Some(r) => Some(r.str),
None => None,
};
}
}
fn do_something(_: Option<String>) -> Option<Result> {
// ...
}
还有一些方法可以使参考版本正常工作;这是一个:
let mut current; // lift this declaration out of the loop so `current` will have
// a lifetime longer than one iteration
for _ in 0..times {
current = do_something(last);
last = match current {
Some(ref r) => Some(&r.str), // borrow from `current` in the loop instead
// of from a newly created String
None => None,
};
}
如果您的代码比示例更复杂并且使用 String
意味着很多潜在的昂贵 .clone()
s,您可能想要这样做。
在您的代码中,尚不清楚 last: Option<&str>
中引用的 String
的所有者应该是谁。您可以引入一个拥有该字符串的额外可变局部变量。但是你会有两个变量:所有者和引用,这似乎是多余的。让 last
成为所有者会简单得多:
struct MyRes {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<String> = None;
for _i in 0..times {
last = do_something(&last).map(|r| r.str);
}
}
fn do_something(_o: &Option<String>) -> Option<MyRes> {
Some(MyRes {
str: "whatever string".to_string(),
})
}
在do_something
中,您可以通过引用传递整个参数,这似乎更符合您的要求。另请注意,命名您自己的结构 Result
不是一个好主意,因为 Result
是编译器(?
-运算符等)中内置的如此普遍的特征。
后续问题:Option<&str>
或Option<String>
?
Option<&str>
和 Option<String>
都有不同的取舍。一种更适合传递字符串文字,另一种更适合传递拥有的 String
s。实际上,我建议两者都不使用,而是使函数泛型覆盖实现 AsRef<str>
的类型 S
。下面是各种方法的比较:
fn do_something(o: &Option<String>) {
let _a: Option<&str> = o.as_ref().map(|r| &**r);
let _b: Option<String> = o.clone();
}
fn do_something2(o: &Option<&str>) {
let _a: Option<&str> = o.clone(); // do you need it?
let _b: Option<String> = o.map(|r| r.to_string());
}
fn do_something3<S: AsRef<str>>(o: &Option<S>) {
let _a: Option<&str> = o.as_ref().map(|s| s.as_ref());
let _b: Option<String> = o.as_ref().map(|r| r.as_ref().to_string());
}
fn main() {
let x: Option<String> = None;
let y: Option<&str> = None;
do_something(&x); // nice
do_something(&y.map(|r| r.to_string())); // awkward & expensive
do_something2(&x.as_ref().map(|x| &**x)); // cheap but awkward
do_something2(&y); // nice
do_something3(&x); // nice
do_something3(&y); // nice, in both cases
}
请注意,并非所有上述组合都是非常惯用的,添加一些只是为了完整性(例如,要求 AsRef<str>
然后构建一个拥有的 String
似乎有点奇怪)。