在try 在 struct 之间移动borrow 的数据时,我遇到了生命周期问题.在更高的层面上,我想做一些类似的事情:

  • 将YML文件读入字符串缓冲区
  • 使用from_str将其反序列化为 struct
  • 使用相同的borrow 数据创建不同的 struct
  • 将其写到一个文件中

下面是我构建的一个示例实现,它提供了生存期错误:

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`.

推荐答案

The first problem with your code is that your trait definition and the actual implementation don't match in their signature, one method does have a lifetime parameter, the other doesn't, this is what the compiler complains about.
To fix this you can just double check that both methods have the same signature (I personally always copy & paste the signature over from the trait's definition or let the LSP server do that for me).

然后有一个问题,&'a Foo<'a>通常是一种代码气味,它实际上从来不是实际的生存期,而且只在少数情况下是可以的.除非您知道您在做什么,否则您应该总是从每个生存期的不同生存期开始,这样编译器就可以准确地告诉您它认为这些生存期是如何关联的.

You don't actually want your borrows to be limited by the reference to the Foo, after all you want to move the reference from it and use that longer lifetime, but that's impossible if you artificially force 'a and 'b of &'a Foo<'b> to be the same by reusing 'a for the inner lifetime.
To fix this replace &'a Foo<'a> with &Foo<'a> everywhere (the outer lifetime can be elided in all places you used it)

The third problem is that your trait definitions don't allow for your requirements, you can't have a function be generic over a lifetime and at the same time fix that same lifetime to some lifetime from the implementation.
To fix it change Visitor and DataBorrowed to accept a lifetime parameter:

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);
}

Rust相关问答推荐

为什么我们不能通过指针算法将Rust原始指针指向任意地址?'

当T不执行Copy时,如何返回Arc Mutex T后面的值?

函数内模块的父作用域的访问类型

我可以在不收集或克隆的情况下,将一个带有Item=(key,val)的迭代器拆分成单独的key iter和val iter吗?

定义只有一些字段可以缺省的 struct

如何从ruust中的fig.toml中读取?

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

Windows 上 ndarray-linalg 与 mkl-stats 的链接时间错误

如何在 Rust 中编写一个通用方法,它可以接受任何可以转换为另一个值的值?

如何从borrow 的异步代码运行阻塞代码?

如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

当在lambda中通过引用传递时,为什么会出现终身/类型不匹配错误?

如何递归传递闭包作为参数?

如何刷新 TcpStream

我什么时候应该使用特征作为 Rust 的类型?

Rust 中的通用 From 实现

如何制作具有关联类型的特征的类型擦除版本?

为什么这个 Trait 无效?以及改用什么签名?

为什么 Rust 中的关联类型需要明确的生命周期注释?

为什么一个整型变量赋值给另一个变量后仍然可以使用?