< 返回版块

NenX 发表于 2025-05-28 17:36

Tags:小弟在学习 Rust 生命周期的时候遇到问题,恳请各路高手相助!为什么下面的代码可以编译通过?在 test_call 中 ff 的类型是 Fn(&'static str) 吗?为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ?为什么 ff1 可以不能接受 &'out str 作为参数


fn main() {
    let s = "out".to_string();
    test_call(s.as_str());
}

fn test_call<'out>(t_str: &'out str) {
    // ff 的类型是 Fn(&'static str) 吗?
    let ff = make_closure("inner");
    // 为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ???
    ff(t_str);

    let ff1:&dyn Fn(&'static str) = &make_closure("inner");
    // ff1 不能接受 &'out str 作为参数 ???

    // ff1(t_str); // 这里出错了
}

fn make_closure<'x>(a: &'x str) -> impl Fn(&'x str) {
    return move |instr: &'x str| println!("{a} {instr}");
}

代码地址

评论区

写评论
作者 NenX 2025-05-30 17:45

谢谢各位大佬!!

tinnkai 2025-05-29 15:32

针对这段代码我做一个简要总结:'out 的生命周期短于 'static,rust生命周期原则短的不能赋给长的

xiaoyaou 2025-05-29 11:12

代码解读我就不写了,只回答你的几个问题:

  1. 在 test_call 中 ff 的类型是 Fn(&'static str) 吗?

不是的,返回位置的impl(RPIT)默认捕获全部的生命周期(可以了解一下最近的use<'_>语法,但与本题无关),所以ff的类型是Fn(&'x str) + 'x,由于'x实体类型没有指定,所以会根据上下文自动推导

  1. 为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ?

Fn(&'static str)不可以接受&'out str的参数,除非明确限定了生命周期边界'out: 'static,否则就违反了生命周期规则(实参存活不够久,形参要求更久)

  1. 为什么 ff1 可以不能接受 &'out str 作为参数

同2。因为ff1被明确限定为Fn(&'static str),所以不能接受比'static活的短的参数

另外考虑你可能有疑惑的几个点

  1. 对于make_closure<'x>方法,因为协变关系,生命周期满足'static: 'x(但并不表示'x = 'static),所以返回的闭包的生命周期依然是'x,且与make_closure的参数只有间接的边界限定关系,在调用ff的时候传入'out的类型,所以'x会被推导为'out,并不破坏生命周期规则。

  2. 对于ff1因为你主动限定了类型,所以'x自然会推导为'static,所以上面问题2和3的回答,应该不需要赘述了

ThinkCodeStudio 2025-05-29 10:26

我凭经验分析的, 可能有误 首先 'out'x 都是正常的生命周期标记, 唯独 'static, 我们都知道 static 的生命周期是和软件的生命周期相同, 软件不关闭, static就一直存在. 那解释的通了, static生命周期就天然大于其他生命周期, 因为static是一直存在的不可能有其他的生命周期比他大. 回到代码解读, 在第一个 ff 这里并不是static生命周期, 还是x生命周期, out和x同大, 所以out可以正常传入, 但是 ff1 显性的标记了static, 由于static 的生命周期大于x, 规则成立, 所以static可以传入x, 但是当再传入out时, out的生命周期是没有static大的, 所以报错了, ff1只能传入static生命周期的参数.

fn main() {
    let s = "out".to_string();
    test_call(s.as_str());
}

fn test_call<'out>(t_str: &'out str) {
    // ff 的类型是 Fn(&'static str) 吗?
    let ff= make_closure("inner");
    // 为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ???
    ff(t_str);

    let ff1:&dyn Fn(&'static str) = &make_closure("inner");
    // ff1 不能接受 &'out str 作为参数 ???

    ff1("t_str");
}

fn make_closure<'x>(a: &'x str) -> impl Fn(&'x str) {
    return move |instr: &'x str| println!("{a} {instr}");
}

通过vscode插件的类型推理也能证明这一点

生命周期标注在这段代码里没有什么用, 把所有的标注都删了都没问题

fn main() {
    let s = "out".to_string();
    test_call(s.as_str());
}

fn test_call(t_str: &str) {
    // ff 的类型是 Fn(&'static str) 吗?
    let ff= make_closure("inner");
    // 为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ???
    ff(t_str);

    let ff1:&dyn Fn(&str) = &make_closure("inner");
    // ff1 不能接受 &'out str 作为参数 ???

    ff1(t_str);
}

fn make_closure(a: &str) -> impl Fn(&str) {
    return move |instr: &str| println!("{a} {instr}");
}

不过我猜你的疑问应该是为什么传入的 "inner" 字符串常量返回的闭包并不是'static的, 我理解的是生命周期标注并不是一个参数, 他要在使用的时候进行分析, 比如字符串常量它的生命周期就是最大的, 没有比他更大的生命周期, 在使用时它不可能被释放或是不存在, 所以在使用时可以不考虑静态生命周期, 去除静态生命周期后就可把两个函数和闭包看作同一个生命周期, 所以去除生命周期标注也不会报错, 不过想要体验生命周期长短的代码我在你的代码基础上改出来, 把静态生命周期引用移除函数, 这样就有了两个不同生命周期的引用参数, 传入函数时就必须要对比两个生命周期的大小

const INNER: &'static str = "inner";
fn main() {
    let s = "out".to_string();
    test_call(s.as_str(), INNER);
}


fn test_call<'out, 'x:'out>(t_str: &'out str, s: &'x str) {
    // ff 的类型是 Fn(&'static str) 吗?
    let ff= make_closure(s);
    // 为什么 Fn(&'static str) 可以接受 &'out str 作为参数 ???
    ff(t_str);

    let ff1:&dyn Fn(&'out str) = &make_closure(s);
    // ff1 不能接受 &'out str 作为参数 ???

    ff1(t_str);
}

fn make_closure<'x>(a: &'x str) -> impl Fn(&'x str) {
    return move |instr: &'x str| println!("{a} {instr}");
}

rustc: 1.87.0

asuper 2025-05-29 09:39

报错信息你是一点不看啊

lifetime may not live long enough type annotation requires that 'out must outlive 'static

你想保持住那个'static,可以这么改,会影响到调用方,但是是必须的,符合rust生命周期的逻辑

fn test_call<'out: 'static>(t_str: &'out str) {
lithbitren 2025-05-28 20:26

不懂,给闭包加个标记,然后交给推断了。

fn main() {
    let s = "out".to_string();
    test_call(s.as_str());
}

fn test_call(t_str: &str) {
    let ff = make_closure("inner");
    ff(t_str);

    let ff1: &dyn Fn(&str) = &make_closure("inner");
    ff1(t_str);
}

fn make_closure(a: &str) -> impl Fn(&str) + '_ {
    move |instr: &str| println!("{a} {instr}")
}
作者 NenX 2025-05-28 18:41

我有点疑惑,ff 的类型是 Fn(&'static str) 吗?

--
👇
bestgopher: 涉及到协变,逆变了吧

bestgopher 2025-05-28 18:21

涉及到协变,逆变了吧

1 共 8 条评论, 1 页