rust 为什么Option::map的参数存在生存期问题?

kd3sttzy  于 2023-05-22  发布在  其他
关注(0)|答案(2)|浏览(150)

我将一个字符串放入Option中,并尝试Map它,例如。要修剪字符串,请执行以下操作:

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);
}
rkttyhzu

rkttyhzu1#

map函数使用迭代值,因此在调用给定闭包之后,这些值将不再存在。不能返回对它们的引用。
最好的解决方案是直接在String上进行就地修剪。可惜的是,目前标准库中还没有。
你的第二个解决方案也可以通过一个小的变化。而不是&String,你需要一个&str

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

mnemlml82#

如上所述,Option::map * 消耗 * 原始值以产生输出值。这是最灵活和最有效的实现,因为您可以将Option<A>转换为Option<B>,而无需克隆原始值。
这种情况下的解决方案是使用Option::as_refOption<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);
}

相关问题