首页 > 解决方案 > 为什么返回字符串切片而不是使用大小会阻止字符串的变异?

问题描述

此代码编译:

fn main() {
    let mut s = String::from("some_string");
    let n = f1(&s);
    s.clear();
    println!("n = {}", n);
}

fn f1(s: &String) -> usize {
    10
}

fn f2(s: &String) -> &str {
    "def"
}

但是,替换对f1()by的调用f2()会导致编译失败。在我看来,两者都f1()f2()了一个不可变的借用,并且s.clear()做了一个可变的借用,所以在这两种情况下我都应该得到编译错误。我错过了什么?

标签: rust

解决方案


这是重现该问题所需的最少代码:

fn f1(s: &String) -> usize { unimplemented!() }

fn f2(s: &String) -> &str { unimplemented!() }

fn main() {
    let mut s = String::from("some_string");
    let n = f1(&s);
    s.clear();
    println!("n = {}", n);
}

生命周期分析是基于函数签名执行的。

你会注意到在上面的代码中我使用unimplemented!()了函数体,问题是完全一样的。这是正常的。

在绝大多数情况下1,函数签名完全指定了函数的接口,无需查看其实现。

作为推论,这也意味着返回类型中的生命周期是否链接到任何参数中的生命周期在签名中完全指定,因此在这种情况下,完整的签名f2是:

fn f2<'a>(s: &'a String) -> &'a str;

f2is "def"(with the 'staticlife) 还是&*s(with the lifetime)的实现'a无关紧要;只有签名很重要,由于省略规则,签名使用相同的生命周期。

1 我知道的一个例外涉及该-> impl Trait功能以及生成的对象是否实现SendSync.


在 的情况下f1,返回类型未链接到参数,因此参数的借用在对 的调用结束时结束f1

fn main() {
    let mut s = String::from("some_string");
    let n = {
        //  Immutable borrow of s starts here.
        f1(&s)
        //  Immutable borrow of s ends here.
    };
    s.clear();
    println!("n = {}", n);
}

在 的情况下f2,返回类型与参数具有相同的生命周期,因此被认为是扩展借用。在 Rust 2015 中,借用将延长,直到返回值超出范围(词法借用);在 Rust 2018 中,借用一直延伸到最后一次使用返回值(非词法借用)。

在您的情况下,两者基本相同:

fn main() {
    let mut s = String::from("some_string");
    let n = {
        //  Immutable borrow of s starts here.
        f2(&s)
    };
    s.clear();  //  Conflicting attempt to mutably borrow s.
    println!("n = {}", n);
    //  Immutable borrow of s ends here.
}

s.clear()您可以通过切换和的顺序来观察差异println!


推荐阅读