< 返回版块

Clownsw 发表于 2024-05-29 16:31

fn test<'a, T: 'a>(req: T) {
    let abc: &'a T = &req;
}
132 | fn test<'a, T: 'a>(req: T) where T: 'a {
    |         --         --- binding `req` declared here
    |         |
    |         lifetime `'a` defined here
133 |     let _: &'a T = &req;
    |            -----   ^^^^ borrowed value does not live long enough
    |            |
    |            type annotation requires that `req` is borrowed for `'a`
134 | }
    | - `req` dropped here while still borrowed

评论区

写评论
作者 Clownsw 2024-05-30 13:04

鲁迅说过 标记的生命周期只是为了取悦编译器,让编译器不要难为我们, 哈哈哈

--
👇
Happy-Feet-hhh: 有帮助就好,Rust的生命周期确实有些麻烦。虽然Rust Book里面用 存活时间长短 来解释生命周期。但编译器最终还是通过类型来判断。

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req; // 子类引用不能指向父类对象,类型不安全('a: 'fn_test --> 'a是子类)
    }
}

即使abc本身确实就是个函数内部的局部变量,可以安全的指向已经移动到函数内部的对象。但Rust编译器还是认死理,只要给出的类型标注超过了内部生命周期就报错。

让我想到当初学Rust时看《Rust圣经》里面作者的一句话:

   `生命周期标注并不会改变任何引用的实际作用域 -- 鲁迅`

今天回答您的问题才让我回想起当初怎么都看不懂的那句话。哈哈

--
👇
Clownsw: 感谢, 您说的没错, test<'a> 这个'a的生命周期超出了test, T被移动到了test内, test结束后就会被销毁掉, 把T改成借用就可以编译过了

--
👇
Happy-Feet-hhh: ```rust fn test<'a, T: 'a>(req: T) { let abc: &'a T = &req; }


我说一下自己的理解吧(有错的话请见谅,但是应该不会差很多),生命周期`lifetime`其实是一种类型,当调用`test`函数的时候编译器会生成相应的具体的函数签名。

其中 T: 'a 意味着 T 是 'a的子类型, 而 'a 标记着一个生命周期作用域(scope),那么 T 是 'a 的子类型这个约束其实就表明:
                   T 的生命周期 必须 >= 'a标记的生命周期   (注: T本身也可能被推断为 &something)

比如:
当我在main函数中,声明一个引用和调用 test 时,会隐式的生成相关生命周期(作用域)的信息:

```rust
fn main() {
    let a = "Hello";
    test(a);
}

展开后:

fn main() {
    // 这里的scope 'a 就是 a 的生命周期
    // 如果这里 T 被推断为 &'a str,那么 scope(&'a str) == scope('a), 满足了 T: 'a
    scope 'a {
        let a: &'a str = "Hello";
        test<'a, &'a str>(a);
    }
}

如果我写的东西题主能看懂的话,那么就能明白函数签名fn test<'a, T: 'a>(req: T)中的 'a 是一个来自函数外部的生命周期,它比函数本身的生命周期要长, 即便 'atest 调用后 马上结束,比如:

// in main
// 'a 的生命周期依然比 test 函数的生命周期要长
scope 'a{
    test<'a, T>();
}

明白这一点后再回到fn test<'a, T: 'a>(req: T)的函数体中,我们将函数体内有关生命周期的东西展开后,得到如下代码:

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req;
    }
}

显然,不能够编译正确,因为 'fn_test 为 函数体内部的生命周期, 在函数调用结束时结束。而 'a 的生命周期大于函数生命周期。我们不能让一个拥有 更长生命周期的引用 指向 更短生命周期的对象, 因为在这可能会造成 悬垂引用。

其实我也不是完全明白编译器怎么处理这些东西的,毕竟我现在也不能完全看懂MIR中间代码的。不过我可以推荐一些网址供题主参考: https://nomicon.purewhite.io/lifetimes.html https://doc.rust-lang.org/reference/subtyping.html#:~:text=Variance%20is%20a%20property%20that%20generic%20types%20have,the%20parameter%20affects%20the%20subtyping%20of%20the%20type.

作者 Clownsw 2024-05-30 13:03

谢谢

--
👇
github.com/shanliu/lsys: &req 在函数的栈上. test的在'a 外,让栈上引用生命周期等于他外面的栈一样长.

作者 Clownsw 2024-05-30 13:01

谢谢

--
👇
Happy-Feet-hhh: 有帮助就好,Rust的生命周期确实有些麻烦。虽然Rust Book里面用 存活时间长短 来解释生命周期。但编译器最终还是通过类型来判断。

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req; // 子类引用不能指向父类对象,类型不安全('a: 'fn_test --> 'a是子类)
    }
}

即使abc本身确实就是个函数内部的局部变量,可以安全的指向已经移动到函数内部的对象。但Rust编译器还是认死理,只要给出的类型标注超过了内部生命周期就报错。

让我想到当初学Rust时看《Rust圣经》里面作者的一句话:

   `生命周期标注并不会改变任何引用的实际作用域 -- 鲁迅`

今天回答您的问题才让我回想起当初怎么都看不懂的那句话。哈哈

--
👇
Clownsw: 感谢, 您说的没错, test<'a> 这个'a的生命周期超出了test, T被移动到了test内, test结束后就会被销毁掉, 把T改成借用就可以编译过了

--
👇
Happy-Feet-hhh: ```rust fn test<'a, T: 'a>(req: T) { let abc: &'a T = &req; }


我说一下自己的理解吧(有错的话请见谅,但是应该不会差很多),生命周期`lifetime`其实是一种类型,当调用`test`函数的时候编译器会生成相应的具体的函数签名。

其中 T: 'a 意味着 T 是 'a的子类型, 而 'a 标记着一个生命周期作用域(scope),那么 T 是 'a 的子类型这个约束其实就表明:
                   T 的生命周期 必须 >= 'a标记的生命周期   (注: T本身也可能被推断为 &something)

比如:
当我在main函数中,声明一个引用和调用 test 时,会隐式的生成相关生命周期(作用域)的信息:

```rust
fn main() {
    let a = "Hello";
    test(a);
}

展开后:

fn main() {
    // 这里的scope 'a 就是 a 的生命周期
    // 如果这里 T 被推断为 &'a str,那么 scope(&'a str) == scope('a), 满足了 T: 'a
    scope 'a {
        let a: &'a str = "Hello";
        test<'a, &'a str>(a);
    }
}

如果我写的东西题主能看懂的话,那么就能明白函数签名fn test<'a, T: 'a>(req: T)中的 'a 是一个来自函数外部的生命周期,它比函数本身的生命周期要长, 即便 'atest 调用后 马上结束,比如:

// in main
// 'a 的生命周期依然比 test 函数的生命周期要长
scope 'a{
    test<'a, T>();
}

明白这一点后再回到fn test<'a, T: 'a>(req: T)的函数体中,我们将函数体内有关生命周期的东西展开后,得到如下代码:

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req;
    }
}

显然,不能够编译正确,因为 'fn_test 为 函数体内部的生命周期, 在函数调用结束时结束。而 'a 的生命周期大于函数生命周期。我们不能让一个拥有 更长生命周期的引用 指向 更短生命周期的对象, 因为在这可能会造成 悬垂引用。

其实我也不是完全明白编译器怎么处理这些东西的,毕竟我现在也不能完全看懂MIR中间代码的。不过我可以推荐一些网址供题主参考: https://nomicon.purewhite.io/lifetimes.html https://doc.rust-lang.org/reference/subtyping.html#:~:text=Variance%20is%20a%20property%20that%20generic%20types%20have,the%20parameter%20affects%20the%20subtyping%20of%20the%20type.

github.com/shanliu/lsys 2024-05-30 10:57

&req 在函数的栈上. test的在'a 外,让栈上引用生命周期等于他外面的栈一样长.

Happy-Feet-hhh 2024-05-30 10:54

有帮助就好,Rust的生命周期确实有些麻烦。虽然Rust Book里面用 存活时间长短 来解释生命周期。但编译器最终还是通过类型来判断。

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req; // 子类引用不能指向父类对象,类型不安全('a: 'fn_test --> 'a是子类)
    }
}

即使abc本身确实就是个函数内部的局部变量,可以安全的指向已经移动到函数内部的对象。但Rust编译器还是认死理,只要给出的类型标注超过了内部生命周期就报错。

让我想到当初学Rust时看《Rust圣经》里面作者的一句话:

   `生命周期标注并不会改变任何引用的实际作用域 -- 鲁迅`

今天回答您的问题才让我回想起当初怎么都看不懂的那句话。哈哈

--
👇
Clownsw: 感谢, 您说的没错, test<'a> 这个'a的生命周期超出了test, T被移动到了test内, test结束后就会被销毁掉, 把T改成借用就可以编译过了

--
👇
Happy-Feet-hhh: ```rust fn test<'a, T: 'a>(req: T) { let abc: &'a T = &req; }


我说一下自己的理解吧(有错的话请见谅,但是应该不会差很多),生命周期`lifetime`其实是一种类型,当调用`test`函数的时候编译器会生成相应的具体的函数签名。

其中 T: 'a 意味着 T 是 'a的子类型, 而 'a 标记着一个生命周期作用域(scope),那么 T 是 'a 的子类型这个约束其实就表明:
                   T 的生命周期 必须 >= 'a标记的生命周期   (注: T本身也可能被推断为 &something)

比如:
当我在main函数中,声明一个引用和调用 test 时,会隐式的生成相关生命周期(作用域)的信息:

```rust
fn main() {
    let a = "Hello";
    test(a);
}

展开后:

fn main() {
    // 这里的scope 'a 就是 a 的生命周期
    // 如果这里 T 被推断为 &'a str,那么 scope(&'a str) == scope('a), 满足了 T: 'a
    scope 'a {
        let a: &'a str = "Hello";
        test<'a, &'a str>(a);
    }
}

如果我写的东西题主能看懂的话,那么就能明白函数签名fn test<'a, T: 'a>(req: T)中的 'a 是一个来自函数外部的生命周期,它比函数本身的生命周期要长, 即便 'atest 调用后 马上结束,比如:

// in main
// 'a 的生命周期依然比 test 函数的生命周期要长
scope 'a{
    test<'a, T>();
}

明白这一点后再回到fn test<'a, T: 'a>(req: T)的函数体中,我们将函数体内有关生命周期的东西展开后,得到如下代码:

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req;
    }
}

显然,不能够编译正确,因为 'fn_test 为 函数体内部的生命周期, 在函数调用结束时结束。而 'a 的生命周期大于函数生命周期。我们不能让一个拥有 更长生命周期的引用 指向 更短生命周期的对象, 因为在这可能会造成 悬垂引用。

其实我也不是完全明白编译器怎么处理这些东西的,毕竟我现在也不能完全看懂MIR中间代码的。不过我可以推荐一些网址供题主参考: https://nomicon.purewhite.io/lifetimes.html https://doc.rust-lang.org/reference/subtyping.html#:~:text=Variance%20is%20a%20property%20that%20generic%20types%20have,the%20parameter%20affects%20the%20subtyping%20of%20the%20type.

作者 Clownsw 2024-05-30 09:47

感谢, 您说的没错, test<'a> 这个'a的生命周期超出了test, T被移动到了test内, test结束后就会被销毁掉, 把T改成借用就可以编译过了

--
👇
Happy-Feet-hhh: ```rust fn test<'a, T: 'a>(req: T) { let abc: &'a T = &req; }


我说一下自己的理解吧(有错的话请见谅,但是应该不会差很多),生命周期`lifetime`其实是一种类型,当调用`test`函数的时候编译器会生成相应的具体的函数签名。

其中 T: 'a 意味着 T 是 'a的子类型, 而 'a 标记着一个生命周期作用域(scope),那么 T 是 'a 的子类型这个约束其实就表明:
                   T 的生命周期 必须 >= 'a标记的生命周期   (注: T本身也可能被推断为 &something)

比如:
当我在main函数中,声明一个引用和调用 test 时,会隐式的生成相关生命周期(作用域)的信息:

```rust
fn main() {
    let a = "Hello";
    test(a);
}

展开后:

fn main() {
    // 这里的scope 'a 就是 a 的生命周期
    // 如果这里 T 被推断为 &'a str,那么 scope(&'a str) == scope('a), 满足了 T: 'a
    scope 'a {
        let a: &'a str = "Hello";
        test<'a, &'a str>(a);
    }
}

如果我写的东西题主能看懂的话,那么就能明白函数签名fn test<'a, T: 'a>(req: T)中的 'a 是一个来自函数外部的生命周期,它比函数本身的生命周期要长, 即便 'atest 调用后 马上结束,比如:

// in main
// 'a 的生命周期依然比 test 函数的生命周期要长
scope 'a{
    test<'a, T>();
}

明白这一点后再回到fn test<'a, T: 'a>(req: T)的函数体中,我们将函数体内有关生命周期的东西展开后,得到如下代码:

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req;
    }
}

显然,不能够编译正确,因为 'fn_test 为 函数体内部的生命周期, 在函数调用结束时结束。而 'a 的生命周期大于函数生命周期。我们不能让一个拥有 更长生命周期的引用 指向 更短生命周期的对象, 因为在这可能会造成 悬垂引用。

其实我也不是完全明白编译器怎么处理这些东西的,毕竟我现在也不能完全看懂MIR中间代码的。不过我可以推荐一些网址供题主参考: https://nomicon.purewhite.io/lifetimes.html https://doc.rust-lang.org/reference/subtyping.html#:~:text=Variance%20is%20a%20property%20that%20generic%20types%20have,the%20parameter%20affects%20the%20subtyping%20of%20the%20type.

Happy-Feet-hhh 2024-05-30 09:15
fn test<'a, T: 'a>(req: T) {
    let abc: &'a T = &req;
}

我说一下自己的理解吧(有错的话请见谅,但是应该不会差很多),生命周期lifetime其实是一种类型,当调用test函数的时候编译器会生成相应的具体的函数签名。

其中 T: 'a 意味着 T 是 'a的子类型, 而 'a 标记着一个生命周期作用域(scope),那么 T 是 'a 的子类型这个约束其实就表明: T 的生命周期 必须 >= 'a标记的生命周期 (注: T本身也可能被推断为 &something)

比如: 当我在main函数中,声明一个引用和调用 test 时,会隐式的生成相关生命周期(作用域)的信息:

fn main() {
    let a = "Hello";
    test(a);
}

展开后:

fn main() {
    // 这里的scope 'a 就是 a 的生命周期
    // 如果这里 T 被推断为 &'a str,那么 scope(&'a str) == scope('a), 满足了 T: 'a
    scope 'a {
        let a: &'a str = "Hello";
        test<'a, &'a str>(a);
    }
}

如果我写的东西题主能看懂的话,那么就能明白函数签名fn test<'a, T: 'a>(req: T)中的 'a 是一个来自函数外部的生命周期,它比函数本身的生命周期要长, 即便 'atest 调用后 马上结束,比如:

// in main
// 'a 的生命周期依然比 test 函数的生命周期要长
scope 'a{
    test<'a, T>();
}

明白这一点后再回到fn test<'a, T: 'a>(req: T)的函数体中,我们将函数体内有关生命周期的东西展开后,得到如下代码:

fn test<'a, T: 'a>(req: T) {
    scope 'fn_test{
        let abc: &'a T = &'fn_test req;
    }
}

显然,不能够编译正确,因为 'fn_test 为 函数体内部的生命周期, 在函数调用结束时结束。而 'a 的生命周期大于函数生命周期。我们不能让一个拥有 更长生命周期的引用 指向 更短生命周期的对象, 因为在这可能会造成 悬垂引用。

其实我也不是完全明白编译器怎么处理这些东西的,毕竟我现在也不能完全看懂MIR中间代码的。不过我可以推荐一些网址供题主参考: https://nomicon.purewhite.io/lifetimes.html https://doc.rust-lang.org/reference/subtyping.html#:~:text=Variance%20is%20a%20property%20that%20generic%20types%20have,the%20parameter%20affects%20the%20subtyping%20of%20the%20type.

1 共 7 条评论, 1 页