< 返回版块

wmlgl 发表于 2021-04-03 23:56

Tags:生命周期

///////// third lib /////////////
struct ThirdPartPage<'a> {
    name: &'a str,
    content: &'a str
}

struct ThirdPartDoc<'a> {
    page: ThirdPartPage<'a>
}

impl <'a> ThirdPartDoc<'a> {
    fn get_page(&self) -> &ThirdPartPage<'a> {
        &self.page
    }
}

///////// resource //////////


trait ResourceTrait<'a> {
    fn get_content(&self) -> &str;
}

struct TirdpartResource<'a> {
    data: &'a ThirdPartPage<'a>
}

impl<'a> ResourceTrait<'a> for TirdpartResource<'a> {
    fn get_content(&self) -> &str {
        self.data.content
    }
}

////////// main logic ////////

struct Owner<'a> {
    doc: Option<ThirdPartDoc<'a>>
}

impl<'a> Owner<'a> {
    fn get_resource(&self) -> Option<Box<dyn ResourceTrait<'a>>> {
        match &self.doc {
            Some(doc) => {
                Some(Box::new(TirdpartResource {
                    data: doc.get_page()
                }))
            },
            None => Option::None
        }
    }
}

fn main() {
    let owner = Owner {
        doc: Some(ThirdPartDoc {
            page: ThirdPartPage {
                name: "page",
                content: "content"
            }
        })
    };
    
    let res = owner.get_resource();
    
    println!("{}", res.unwrap().get_content());
}


third lib是外部库,这里是错误信息:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:42:15
   |
42 |         match &self.doc {
   |               ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 41:21...
  --> src/main.rs:41:21
   |
41 |     fn get_resource(&self) -> Option<Box<dyn ResourceTrait<'a>>> {
   |                     ^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:42:15
   |
42 |         match &self.doc {
   |               ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 40:6...
  --> src/main.rs:40:6
   |
40 | impl<'a> Owner<'a> {
   |      ^^
note: ...so that the types are compatible
  --> src/main.rs:44:22
   |
44 |                   Some(Box::new(TirdpartResource {
   |  ______________________^
45 | |                     data: doc.get_page()
46 | |                 }))
   | |__________________^
   = note: expected `ResourceTrait<'a>`
              found `ResourceTrait<'_>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

评论区

写评论
johnmave126 2021-04-04 05:06

分析get_resource这个函数:

get_resource(&self): 函数接受一个参数,类型是Self也即Owner<'a>的引用,这个引用的生命周期是隐式的,我们姑且记为'_,所以函数的实际签名是get_resource(&'_ self)

match &self.doc: 从self里借走了doc,那么这个借的周期是什么呢,在这个节点,编译器只知道这个周期需要不短于match的block,不长于'_,假设这个周期是'b

Some(Box::new(TirdpartResource { data: doc.get_page() })): doc.get_page()拿了一个生命周期'b&ThirdPartDoc<'a>,返回一个&'b ThirdPartPage<'a>用来构造TirdpartResource。虽然看起来TirdpartResource<'a>是说这个结构里的所有周期都必须是'a,但其实'a是泛型参数,所以编译器只需要选取输入生命周期中最局限的生命周期就可以了,假定这个生命周期是'c,我们知道的是'c必须不长于'b'a

现在来推断一下生命周期。'c不长于'b不长于'_,但是同时根据返回值类型匹配,'c='a,所以'a必须不长于'_,但是从函数签名中无法保证这一点,所以无法编译。另一种解读的方法就是,根据前者'b应当设定为'_,但后者表明'b应当设定为'a,产生了矛盾。

那么如何修复,只需要保证'_不短于'a就可以了,这里还有个需要注意的小问题,就是Box这种堆上内存的生命周期需要显式标注。

方案1 让'_='a

fn get_resource(&'a self) -> Option<Box<dyn ResourceTrait<'a> + 'a>>

方案2 让'_ >= 'a:

fn get_resource<'b: 'a>(&'b self) -> Option<Box<dyn ResourceTrait<'a> + 'b>>
1 共 1 条评论, 1 页