代码之家  ›  专栏  ›  技术社区  ›  ralh

尝试更新循环中的选项<&str>时获取“借用时丢弃的临时值”

  •  1
  • ralh  · 技术社区  · 5 年前

    我正在尝试实现一个常用的模式——在下一个循环迭代中使用前一个循环迭代的结果。例如,在需要给出上一页最后一个值的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 (AT) &r.str.to_owned() )虽然我做了很多其他的尝试,但都没有用。

    我发现让它真正工作的唯一方法是创建某种本地的 tmp_str 变量并执行如下黑客操作:

    match current {
        Some(r) => {
            tmp_str.clone_from(&r.str);
            last = Some(&tmp_str);
        }
        None => {
            last = None;
        }
    }
    

    但这并不是应该这样做的。

    2 回复  |  直到 5 年前
        1
  •  3
  •   Andrey Tyukin    5 年前

    在您的代码中,不清楚谁是 String 引用于 last: Option<&str> 应该是。您可以引入一个拥有该字符串的额外可变局部变量。但是您将有两个变量:所有者和引用,这看起来是多余的。只做一个 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 是个坏主意,因为 结果 这种普遍的特性深深地嵌入了编译器中吗?( ? -操作员等)。


    后续问题: Option<&str> Option<String> ?

    两个 选项<&str> 选项<字符串> 有不同的权衡。一个更适合传递字符串文本,另一个更适合传递所拥有的文本。 实际上,我建议两者都不要使用,而是使函数在类型上为泛型 S 那工具 AsRef<str> . 以下是各种方法的比较:

    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> 然后建立一个拥有的 似乎有点奇怪)。

        2
  •  2
  •   trent oli_obk    5 年前

    r.str.to_owned() 是临时值。 You can take a reference to a temporary ,但由于临时值通常会在最里面的封闭语句的结尾处被删除(销毁),因此引用在该点处会悬空。在这种情况下,“最里面的封闭语句”要么是循环的最后一行,要么是循环体本身——我不确定在这里到底应用了哪一个,但这并不重要,因为不管怎样,您都要 last 包含对 String 很快就会掉下来 最后的 不可用的。编译器可以阻止您在循环的下一次迭代中再次使用它。

    最简单的解决办法就是 最后的 一个引用——在这个例子中,它是不必要或不可取的。只使用 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,
        };
    }
    

    如果您的代码比示例和 意味着很多潜在的昂贵 .clone() S.