我可以限制结构的终生污染吗?

时间:2017-05-26 21:41:14

标签: struct reference iterator rust lifetime

我有一个包含一些东西的结构。我为该结构实现了Iterator特征,并在结构中返回一个对内部数据的引用元组。这就要求我至少用生命诠释一些东西。我想要的是最小化生命周期注释,特别是当涉及到原始结构作为成员的其他结构时。

一些代码:

pub struct LogReader<'a> {
    data:String,
    next_fn:fn(&mut LogReader)->Option<(&'a str,&'a [ConvertedValue])>,
//...
}

pub struct LogstreamProcessor {
    reader: LogReader, // doesn't work without polluting LogstreamProcessor with lifetimes
//...
}

impl<'a> Iterator for LogReader<'a > {
    type Item = (&'a str,&'a[ConvertedValue]);

    fn next(&mut self) -> Option<(&'a str,&'a[ConvertedValue])>{(self.next_fn)(self)}

}

impl <'a> LogReader<'a> {
    pub fn new(textFile:Option<bool>) -> LogReader<'a> {
        LogReader {
            next_fn:if textFile.unwrap_or(false) { LogReader::readNextText }else{ LogReader::readNextRaw },
            data: "blah".to_string()
        }
    }

    fn readNextText(&mut self)->Option<(&str,&[ConvertedValue])>{unimplemented!();}
    fn  readNextRaw(&mut self)->Option<(&str,&[ConvertedValue])>{unimplemented!();}
}

1 个答案:

答案 0 :(得分:1)

  

我可以限制结构的终身污染吗?

通常,如果您在任何结构的字段中使用它们,那么就不能。它们是出于很好的理由而显而易见(参见Why are explicit lifetimes needed in Rust?),一旦你有一个包含需要显式生命周期的对象的结构,那么它们必须被传播。

请注意,结构的使用者通常不关心这一点,因为编译器会强制执行具体的生命周期:

struct NameRef<'a>(&'a str);

let name = NameRef("Jake"); // 'a is 'static

通过使用next的定义,还可以略微减轻Self::Item实施的“噪音”。

impl<'a> Iterator for LogReader<'a > {
    type Item = (&'a str,&'a[ConvertedValue]);

    fn next(&mut self) -> Option<Self::Item> {
        (self.next_fn)(self)
    }
}

然而,您的担忧实际上隐藏了一个更严重的问题:与您提到的不同,next返回的值不一定是结构中的内部数据。它们实际上和通用生命周期'a一样长,并且LogReader内的任何内容实际上都不受该生命周期的约束。

这意味着两件事:

(1)我可以传递一个完全不同的函数,它可以正常工作:

static NO_DATA: &[()] = &[()];
fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> {
    Some(("wat", NO_DATA))
}

(2)即使我希望我的函数从日志阅读器的内部数据中返回一些东西,它也不会起作用,因为它的生命周期根本不匹配。让我们试试看看会发生什么:

static DATA: &[()] = &[()];

fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> {
    Some((&reader.data[0..4], DATA))
}

fn main() {
    let mut a = LogReader {
      data: "This is DATA!".to_owned(),
      next_fn: my_next_fn
    };

    println!("{:?}", a.next());
}

编译器会抛弃你:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/main.rs:26:12
   |
26 |     Some((&reader.data[0..4], DATA))
   |            ^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 25:88...
  --> src/main.rs:25:89
   |
25 |   fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> {
   |  _________________________________________________________________________________________^ starting here...
26 | |     Some((&reader.data[0..4], DATA))
27 | | }
   | |_^ ...ending here
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:26:12
   |
26 |     Some((&reader.data[0..4], DATA))
   |            ^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the body at 25:88...
  --> src/main.rs:25:89
   |
25 |   fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> {
   |  _________________________________________________________________________________________^ starting here...
26 | |     Some((&reader.data[0..4], DATA))
27 | | }
   | |_^ ...ending here
note: ...so that expression is assignable (expected std::option::Option<(&'a str, &'a [()])>, found std::option::Option<(&str, &[()])>)
  --> src/main.rs:26:5
   |
26 |     Some((&reader.data[0..4], DATA))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

...匿名生命#1是日志阅读器的生命周期。强制&mut LogReader生命周期'a&'a mut LogReader<'a>)会在尝试实施Iterator时导致更长的生命周期问题。这基本上缩小到'aLogReader自身值的引用不相容的事实。

那么,我们应该如何解决这个问题?

  

但这不会改变返回类型具有引用的事实,因此生命周期注释会进入其中

虽然这不准确(因为在某些情况下可能会出现生命周期缺失),但这会给出解决方案的提示:要么避免返回引用,要么将数据委托给单独的对象,以便'a可以绑定到该对象的生命周期。问题答案的最后部分是Iterator returning items by reference, lifetime issue