Rust生存期不匹配- 'a不一定比这里定义的生存期更长

twh00eeo  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(121)

当尝试在结构体之间移动借用的数据时,我遇到了生存期问题。在高层次上,我想做一些类似的事情:

  • 读取一个yml文件到字符串缓冲区
  • 使用from_str将其转换为结构体
  • 从相同的借用数据创建不同的结构
  • 把它写进一个文件里

下面是我构建的给出生存期错误的示例实现:
Rust playground link

use serde::{Serialize, Deserialize};

#[derive(Deserialize, Debug, Serialize)]
pub struct Foo<'a> {
  name: &'a str
}

impl<'a> Foo<'a> {
    
    pub fn new(name: &'a str) -> Self {
        Self { name }
    }
}

#[derive(Deserialize, Debug, Serialize)]
pub struct Bar<'a> {
  name: &'a str
}

impl<'a> Bar<'a> {
    
    pub fn new(name: &'a str) -> Self {
        Self { name }
    }
}

pub trait Visitor {
    type Value;
    fn visit_table_borrowed<'a>(&mut self, t: &'a Foo<'a>) {
        let _ = t;
    }
}

pub trait DataBorrowed {
    fn accept<V: Visitor>(&self, visitor: &mut V);
}

impl<'a> DataBorrowed for Foo<'a> {
    fn accept<V: Visitor>(&self, visitor: &mut V) {
        visitor.visit_table_borrowed(self)
    }
}

impl<'a> Visitor for Bar<'a> {
    type Value = Bar<'a>;
    fn visit_table_borrowed(&mut self, t: &'a Foo<'a>) {
       self.name = t.name;
    }
}

impl<'a> From<Foo<'a>> for Bar<'a> {
    fn from(dt: Foo<'a>) -> Self {
        let mut table = Bar::new("bar");
        dt.accept(&mut table);
        table
    }
}

fn main() {
    let f = Foo::new("foo");
    let b: Bar = Bar::from(f);
    
}

字符串
错误:

error[E0308]: method not compatible with trait
  --> src/main.rs:49:5
   |
49 |     fn visit_table_borrowed(&mut self, t: &'a Foo<'a>) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected signature `fn(&mut Bar<'a>, &'a Foo<'a>)`
              found signature `fn(&mut Bar<'a>, &'a Foo<'a>)`
note: the lifetime `'a` as defined here...
  --> src/main.rs:49:5
   |
49 |     fn visit_table_borrowed(&mut self, t: &'a Foo<'a>) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
  --> src/main.rs:47:6
   |
47 | impl<'a> Visitor for Bar<'a> {
   |      ^^

For more information about this error, try `rustc --explain E0308`.

sczxawaw

sczxawaw1#

代码的第一个问题是trait定义和实际实现的签名不匹配,一个方法有生命周期参数,另一个没有,这是编译器抱怨的。
要解决这个问题,你可以仔细检查两个方法是否有相同的签名(我个人总是从trait的定义中复制并粘贴签名,或者让LSP服务器为我做这件事)。
还有一个问题是&'a Foo<'a>实际上是一种代码气味,它实际上从来都不是实际的生存期,只有在少数情况下是可以的。除非你知道你在做什么,否则你应该总是从每个生存期漏洞的不同生存期开始,这样编译器就可以告诉你它认为这些生存期是如何联系的。
实际上,您并不希望您的借用受到对Foo的引用的限制,毕竟您希望从它移动引用并使用更长的生存期,但是如果您人为地强制&'a Foo<'b>'a'b相同,通过重用'a作为内部生存期,这是不可能的。
要解决这个问题,请在所有地方将&'a Foo<'a>替换为&Foo<'a>(可以在使用它的所有地方省略外部生存期)
第三个问题是,你的trait定义不允许你的需求,你不能让一个函数在生命周期内是泛型的,同时将同一生命周期固定为实现的某个生命周期。
要修复它,请更改VisitorDataBorrowed以接受生命周期参数:

pub trait Visitor<'a> {
    type Value;
    fn visit_table_borrowed(&mut self, t: &Foo<'a>) {
        let _ = t;
    }
}

pub trait DataBorrowed<'a> {
    fn accept<V: Visitor<'a>>(&self, visitor: &mut V);
}

字符串
下面是一个完整的版本,解决了所有三个问题:

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Serialize)]
pub struct Foo<'a> {
    name: &'a str,
}

impl<'a> Foo<'a> {
    pub fn new(name: &'a str) -> Self {
        Self { name }
    }
}

#[derive(Deserialize, Debug, Serialize)]
pub struct Bar<'a> {
    name: &'a str,
}

impl<'a> Bar<'a> {
    pub fn new(name: &'a str) -> Self {
        Self { name }
    }
}

pub trait Visitor<'a> {
    type Value;
    fn visit_table_borrowed(&mut self, t: &Foo<'a>) {
        let _ = t;
    }
}

pub trait DataBorrowed<'a> {
    fn accept<V: Visitor<'a>>(&self, visitor: &mut V);
}

impl<'a> DataBorrowed<'a> for Foo<'a> {
    fn accept<V: Visitor<'a>>(&self, visitor: &mut V) {
        visitor.visit_table_borrowed(self)
    }
}

impl<'a> Visitor<'a> for Bar<'a> {
    type Value = Bar<'a>;
    fn visit_table_borrowed(&mut self, t: &Foo<'a>) {
        self.name = t.name;
    }
}

impl<'a> From<Foo<'a>> for Bar<'a> {
    fn from(dt: Foo<'a>) -> Self {
        let mut table = Bar::new("bar");
        dt.accept(&mut table);
        table
    }
}

fn main() {
    let f = Foo::new("foo");
    let b: Bar = Bar::from(f);
}

相关问题