Playground

语境

我想在将文本文件的行传递给函数f之前对其进行预处理。我可以这样:

pub fn example0a<B: BufRead, F: Fn(&str)>(bufread: B, f: F) {
    let name = Regex::new("John Doe").unwrap();

    for line in bufread.lines() {
        let line = line.unwrap();
        let pre_processed_line = name.replace_all(&line, "XXX");
        f(&pre_processed_line);
    }
}

但是我需要使用for_each方法生成一个对象,可以直接将f传递给该对象。我的第一个想法是使用map方法生成一个迭代器:
// does not compile
pub fn example0b<B: BufRead>(bufread: B) -> impl Iterator {
    let name = Regex::new("John Doe").unwrap();

    bufread.lines().map(move |line| {
        let line = line.unwrap();
        let pre_processed_line = name.replace_all(&line, "XXX");
        &pre_processed_line as &str;
    })
}

这不会编译,因为line以及pre_processed_line的生存时间不足以使其从迭代器的next方法中返回。一种选择是返回pre_processed_line.to_string(),但这不好,因为它会克隆replace_all尚未修改的所有行,我想避免这种行。

我的第一个结构

我决定实现一个包含BufRead和预处理功能的结构,并提供for_each方法。我旨在使它尽可能通用,因此实际上,它接受任何类型的任何迭代器,只要预处理功能可以将其转换为&str即可。
pub struct TransformedStrStream<S, FT>
where
    S: Iterator,
    FT: FnMut(S::Item, &mut FnMut(&str)),
{
    source: S,
    transf: FT,
}

impl<S, FT> TransformedStrStream<S, FT>
where
    S: Iterator,
    FT: FnMut(S::Item, &mut FnMut(&str)),
{
    pub fn for_each<F>(self, mut f: F)
    where
        F: FnMut(&str),
    {
        let source = self.source;
        let mut transf = self.transf;
        source.for_each(move |line| transf(line, &mut f));
    }
}

我可以用类似于上面示例的方式实例化该结构:
pub fn example1<B: BufRead>(bufread: B, name: Regex) {
    let _ = TransformedStrStream {
        source: bufread.lines(),
        transf: move |line, f| {
            let line = line.unwrap();
            let repl = name.replace_all(&line, "XXX");
            f(&repl as &str)
        },
    };
}

我的问题

我认为上面的结构是一个很好的抽象,可以进一步抽象以生成任何类型的值(而不是&str)。

我试图用类型参数&str替换T:
pub struct TransformedStream<S, FT, T>
where
    S: Iterator,
    FT: FnMut(S::Item, &mut FnMut(T)),
{
    source: S,
    transf: FT,
    phantom: PhantomData<T>,
}

impl<S, FT, T> TransformedStream<S, FT, T>
where
    S: Iterator,
    FT: FnMut(S::Item, &mut FnMut(T)),
{
    pub fn for_each<F>(self, mut f: F)
    where
        F: FnMut(T),
    {
        let source = self.source;
        let mut transf = self.transf;
        source.for_each(move |line| transf(line, &mut f));
    }
}

不幸的是,我上面的例子不再编译了:
pub fn example2<B: BufRead>(bufread: B, name: Regex) {
    let _ = TransformedStream {
        source: bufread.lines(),
        transf: move |line, f| {
            let line = line.unwrap();
            let repl = name.replace_all(&line, "XXX");
            f(&repl as &str)
        },
        phantom: PhantomData,
    };
}

error[E0597]: `line` does not live long enough
  --> src/lib.rs:37:42
   |
37 |             let repl = name.replace_all(&line, "XXX");
   |                                          ^^^^ borrowed value does not live long enough
38 |             f(&repl as &str)
39 |         },
   |         - `line` dropped here while still borrowed
40 |         phantom: PhantomData,
41 |     };
   |     - borrowed value needs to live until here

error[E0597]: `repl` does not live long enough
  --> src/lib.rs:38:16
   |
38 |             f(&repl as &str)
   |                ^^^^ borrowed value does not live long enough
39 |         },
   |         - `repl` dropped here while still borrowed
40 |         phantom: PhantomData,
41 |     };
   |     - borrowed value needs to live until here

我认为linerepl的生存时间足以由f处理,就像&str版本一样。对于上面的迭代器示例,使用repl.to_string()可以满足编译器的要求,但是我不想克隆每一行。

我的直觉是问题出在我为满足编译器而必须添加到结构中的PhantomData<T>中。是否像我有一个T类型的字段一样,限制了T的生存期(生存时间与包含的struct一样长)?我试图用PhantomData<*const T>替换它,我认为这可能不会限制生存期,但是并不能解决我的问题...

为什么第二个版本不能编译?我该如何运作?

最佳答案

代替T,使用&T。它是原始&str版本的更直接的翻译,因此您可以确定在进行更改后它可以工作。它确实:

pub struct TransformedStream<S, FT, T>
where
    S: Iterator,
    T: ?Sized,
    FT: FnMut(S::Item, &mut FnMut(&T)),
{
    source: S,
    transf: FT,
    phantom: PhantomData<*const T>,
}

impl<S, FT, T> TransformedStream<S, FT, T>
where
    S: Iterator,
    T: ?Sized,
    FT: FnMut(S::Item, &mut FnMut(&T)),
{
    pub fn for_each<F> (self, mut f: F) where F: FnMut(&T) {
        let source = self.source;
        let mut transf = self.transf;
        source.for_each(move |line| transf(line, &mut f));
    }
}

关于generics - 我可以使这个结构更通用吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52327942/

10-11 22:59
查看更多