为什么Option::map的参数会有生命周期问题?

4

我将一个字符串放入一个Option中,并尝试对其进行映射,例如修剪字符串:

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(s);

    let x = s_opt.map(|z| z.trim());
    //     let x = s_opt.map(|z| z.trim().to_string());

    println!("'{:?}'", x);
}

编译器显示了生命周期错误

error[E0597]: `z` does not live long enough
 --> src/main.rs:5:27
  |
5 |     let x = s_opt.map(|z| z.trim());
  |                           ^      - `z` dropped here while still borrowed
  |                           |
  |                           borrowed value does not live long enough
...
9 | }
  | - borrowed value needs to live until here

这很清楚,因为z仅在闭包中定义,且参数通过值传递。

由于原始变量s在整个块中都存在,编译器不应该能够弄清楚z实际上是s吗?

我唯一能让它工作的方法是添加to_string(请参见注释行),但那样我就会创建一个新的字符串对象。

我找到的另一种解决方案是将s_opt作为Option<&String>类型(请参见第二个代码块),但由于函数无法返回此类类型,因此这不是一个真正的选择。

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(&s);

    let x = s_opt.map(|z| z.trim());
    println!("'{:?}'", x);
}

我有什么遗漏的吗?或者默认实现的map是否类似于此更好?

fn my_map<'r, F>(o: &'r Option<String>, f: F) -> Option<&'r str>
where
    F: Fn(&'r String) -> &'r str,
{
    match *o {
        None => None,
        Some(ref x) => Some(f(x)),
    }
}

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(s);

    let x = my_map(&s_opt, |x| x.trim());
    println!("'{:?}'", x);
}
2个回答

3
< p > map 函数消耗迭代值,因此在调用给定闭包后它们不再存在。你不能返回对它们的引用。

最好的解决方案是直接在 String 上进行原地修剪操作。可惜目前标准库中没有提供这个函数。

你的第二个解决方案也可以,只需稍加改动。取 &str 而不是 &String

fn main() {
    let s = "text".to_string();
    let s_opt = Some(s.as_str());
    let x = s_opt.map(|z| z.trim());
    println!("{:?}", x);
}

谢谢PEPP的回复。实际上我在使用std::io时遇到了问题。返回的类型是IoResult<String>(例如read_line)。因此第二种解决方案无法使用。因此,我必须使用as_slice().trim().to_string()解决方案或者使用try!宏并将其包装在额外的函数中。我是对的吗? - wiep

2
正如所述,Option::map 消耗 原始值以生成输出值。这是最灵活和高效的实现方式,因为您可以将Option<A>转换为Option<B>而无需克隆原始值。
在这种情况下的解决方案是使用Option::as_ref将您的Option<String>(实际上是&Option<String>)转换为Option<&String>。一旦您拥有了Option<&String>,您就可以消耗它,而不会失去对原始Option<String>的所有权:
fn main() {
    let s = Some("     i want to be trimmed    ".to_string());

    let x = s.as_ref().map(|z| z.trim());

    println!("{:?}", x);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接