首页 文章

使用具有显式生命周期的特征时,无法推断借用表达式的生存期

提问于
浏览
0
use std::io::BufReader;
struct Foo {
    buf: [u8, ..10]
}

trait Bar<'a> {
    fn test(&self, arg: BufReader<'a>) {}
}

impl<'a, T: Bar<'a>> Foo {
    fn bar(&'a mut self, t: T) {
        t.test(BufReader::new(&self.buf));
        let b = &mut self.buf;
    }

    fn baz(&self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

fn main() {}

上面的代码无法编译,错误消息:

lifetimes.rs:17:31: 17:40 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
                                              ^~~~~~~~~
lifetimes.rs:16:5: 18:6 help: consider using an explicit lifetime parameter as shown: fn baz(&'a self, t: T)
lifetimes.rs:16     fn baz(&self, t: T) {
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
lifetimes.rs:18     }
error: aborting due to previous error

但是,如果我添加命名的生命周期参数,我不能在调用 test 之后借用 buf 字段,如 fn bar 中所示 . 注释 fn baz 并尝试编译结果:

lifetimes.rs:13:22: 13:30 error: cannot borrow `self.buf` as mutable because it is also borrowed as immutable
lifetimes.rs:13         let b = &mut self.buf;
                                     ^~~~~~~~
lifetimes.rs:12:32: 12:40 note: previous borrow of `self.buf` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.buf` until the borrow ends
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
                                               ^~~~~~~~
lifetimes.rs:14:6: 14:6 note: previous borrow ends here
lifetimes.rs:11     fn bar(&'a mut self, t: T) {
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
lifetimes.rs:13         let b = &mut self.buf;
lifetimes.rs:14     }
                    ^
error: aborting due to previous error

我对此的理解是,通过将命名的生命周期 'a 添加到 &'a mut self 参数,只要 self 引用有效, BufReader 所采用的引用就具有生命周期,直到函数结束 . 这与之后在线上的 self.buf 的可变借用相冲突 .

但是,我不确定为什么我需要 self 上的命名生命周期参数 . 在我看来, BufReader 引用应该只能在 t.test 方法调用的生命周期中存在 . 编译器是否在抱怨,因为必须确保 self.buf 借用只要 &self 借用?我怎么会这样做而仍然只是在方法调用的生命周期中借用它?

任何有关修复此问题和了解更多有关语义的帮助将非常感谢!

更新

所以我仍在调查这个问题,我发现this test casethis issue基本上显示了我想要做的事情 . 我非常想了解为什么测试用例链接指向的错误是错误的 .

我可以在问题中看到rustc输出试图指出错误是什么,但我无法理解究竟是什么意思 .

2 回答

  • 1

    Edit

    我要在这里复制我的评论:

    我原本以为在trait / struct / enum中添加一个生命周期或泛型参数是将它放在特征中的每个方法上的简写,但我是 wrong . 我目前的理解是,当该项需要参与生命周期时,您可以为trait / struct / enum添加生命周期,这可能是因为它存储了具有该生命周期的引用 .

    struct Keeper<'a> {
        counts: Vec<&'a i32>,
    }
    
    impl<'a> Keeper<'a> {
        fn add_one(&mut self, count: &'a i32) {
            if *count > 5 {
                self.counts.push(count);
            }
        }
    
        fn add_two<'b>(&mut self, count: &'b i32) -> i32 {
            *count + 1
        }
    }
    
    fn main() {
        let mut cnt1 = 1;
        let mut cnt2 = 2;
        let mut k = Keeper { counts: Vec::new() };
    
        k.add_one(&cnt1);
        k.add_two(&cnt2);
    
        // cnt1 += 1; // Errors: cannot assign to `cnt1` because it is borrowed
        cnt2 += 1; // Just fine
    
        println!("{}, {}", cnt1, cnt2)
    }
    

    在这里,我们为 Keeper 添加了一个生命周期,因为它可能存储它给出的引用 . 当我们调用 add_one 时,借用检查器必须假设引用存储良好,所以一旦我们调用该方法,我们就不能再改变该值 .

    另一方面, add_two 创建了一个只能应用于该函数调用的新生命周期,因此借用检查器知道一旦函数返回,它就是One True Owner .

    结果是,如果你需要存储一个引用,那么在这个级别你无能为力 . Rust无法确保您的安全,这是需要认真对待的事情 .

    但是,我打赌你不需要存储引用 . 将 <'a, T: Bar<'a>>impl 移动到 fn ,你会很高兴 .

    换句话说:如果你的特质或结构没有 require ,我打赌你永远不应该 impl<A> . 将泛型放在方法上 .

    Original

    这编译,但我不是100%确定它符合你的意图:

    impl Foo {
        fn baz<'a, T: Bar<'a>>(&'a self, t: T) {
            t.test(BufReader::new(&self.buf));
        }
    }
    

    I fell into this trap myself,所以我会粘贴我被告知的内容:

    impl块中的所有内容都已参数化 . 我实际上从未见过将类型参数添加到不属于特征或类型定义的impl块本身 . 参数化需要它的各个方法更为常见 .

    也许其他评论/答案可以帮助进一步详细解释 .

  • 2

    删除所有显式生命周期也有效 . 我发现只有当我确定需要它时才会添加生命周期(即,指定两个生命周期应该在给定点相交,编译器无法知道) .

    我不确定你到底想要什么,但是这个编译(在每晚生成0.13.0秒(cc19e3380 2014-12-20 20:00:36 0000)) .

    use std::io::BufReader;
    struct Foo {
        buf: [u8, ..10]
    }
    
    trait Bar {
        fn test(&self, arg: BufReader) {}
    }
    
    impl<T: Bar> Foo {
        fn bar(&mut self, t: T) {
            t.test(BufReader::new(&self.buf));
            let b = &mut self.buf;
        }
    
        fn baz(&self, t: T) {
            t.test(BufReader::new(&self.buf));
        }
    }
    

相关问题