str 类型的生命周期/借用问题

Vai*_*nne 4 lifetime rust borrowing

为什么这段代码会编译?

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let x = "eee";
    let &m;
    {
        let y = "tttt";
        m = longest(&x, &y);
    }
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

对我来说,由于生命周期,应该存在编译错误。如果我用 编写相同的代码i64,则会出现错误。

fn ooo<'a>(x: &'a i64, y: &'a i64) -> &'a i64 {
    if x > y {
        x
    } else {
        y
    }
}

fn main() {
    let x = 3;
    let &m;
    {
        let y = 5;
        m = ooo(&x, &y);
    }
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

错误是:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let x = "eee";
    let &m;
    {
        let y = "tttt";
        m = longest(&x, &y);
    }
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

SCa*_*lla 6

为了理解这一点,我们需要知道一些事情。第一个是字符串文字的类型。任何字符串文字(如"foo")都具有类型&'static str。这是对字符串切片的引用,而且,它是一个静态引用。这种引用持续整个程序长度,并且可以根据需要强制到任何其他生命周期。

这意味着在您的第一段代码中, x 和 y 已经都是引用并且具有 type &'static str。究其原因,电话longest(&x, &y)仍然有效(即使&x&y有型&&'static str)是由于DEREF胁迫。 为了使类型匹配,longest(&x, &y)真的很不加糖longest(&*x, &*y)

让我们分析第一段代码中的生命周期。

fn main() {
    // x: &'static str
    let x = "eee";
    // Using let patterns in a forward declaration doesn't really make sense
    // It's used for things like
    // let (x, y) = fn_that_returns_tuple();
    // or
    // let &x = fn_that_returns_reference();
    // Here, it's the same as just `let m;`.
    let &m;
    {
        // y: &'static str
        let y = "tttt";
        // This is the same as `longest(x, y)` due to autoderef
        // m: &'static str
        m = longest(&x, &y);
    }
    // `m: &static str`, so it's still valid here
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

(操场)

使用let &m;您可能意味着let m: &str强制其类型。我认为这实际上可以确保引用的生命周期m从该前向声明开始。但既然m有类型&'static str,那就无所谓了。


现在让我们看看第二个版本i64

fn main() {
    // x: i64
    // This is a local variable
    // and will be dropped at the end of `main`.
    let x = 3;
    // Again, this doesn't really make sense.
    let &m;
    // If we change it to `let m: &i64`, the error changes,
    // which I'll discuss below.
    {
        // Call the lifetime roughly corresponding to this block `'block`.
        // y: i64
        // This is a local variable,
        // and will be dropped at the end of the block.
        let y = 5;
        // Since `y` is local, the lifetime of the reference here
        // can't be longer than this block.
        // &y: &'block i64
        // m: &'block i64
        m = ooo(&x, &y);
    } // Now the lifetime `'block` is over.
      // So `m` has a lifetime that's over
      // so we get an error here.
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

(操场)

如果我们更改mto的声明let m: &i64(这就是我认为你的意思),错误就会改变。

fn main() {
    // x: &'static str
    let x = "eee";
    // Using let patterns in a forward declaration doesn't really make sense
    // It's used for things like
    // let (x, y) = fn_that_returns_tuple();
    // or
    // let &x = fn_that_returns_reference();
    // Here, it's the same as just `let m;`.
    let &m;
    {
        // y: &'static str
        let y = "tttt";
        // This is the same as `longest(x, y)` due to autoderef
        // m: &'static str
        m = longest(&x, &y);
    }
    // `m: &static str`, so it's still valid here
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

(操场)

所以现在我们明确希望m持续时间与外部块一样长,但我们不能y持续那么长时间,所以错误发生在调用ooo.


由于这两个程序都在处理文字,我们实际上可以编译第二个版本。为此,我们必须利用静态推广。可以在Rust 1.21 公告(这是引入此版本的版本)或在此问题中找到对这意味着什么的一个很好的总结。

简而言之,如果我们直接引用一个字面值,该引用可能会被提升为静态引用。也就是说,它不再引用局部变量。

fn ooo<'a>(x: &'a i64, y: &'a i64) -> &'a i64 {
    if x > y {
        x
    } else {
        y
    }
}

fn main() {
    // due to promotion
    // x: &'static i64
    let x = &3;
    let m;
    {
        // due to promotion
        // y: &'static i64
        let y = &5;
        // m: &'static i64
        m = ooo(x, y);
    }
    // So `m`'s lifetime is still active
    println!("ahahah: {}", m);
}
Run Code Online (Sandbox Code Playgroud)

(操场)