我可以使这个结构更通用吗?
Can I make this struct more generic?
上下文
我想在将文本文件的行传递给函数之前对其进行预处理 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
没有足够长的时间被 return 从迭代器的 next
方法中编辑出来。一种选择是 return 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
)。
我试图用类型参数 T
:
替换 &str
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
在我看来,line
和 repl
的寿命足以被 f
处理,就像 &str
版本一样。至于上面的迭代器示例,使用 repl.to_string()
可以满足编译器的要求,但我不想克隆每一行。
我的直觉是问题出在 PhantomData<T>
上,我必须将它添加到我的结构中以满足编译器的要求。它是否限制 T
的生命周期(与包含的结构一样长),就像我有一个类型为 T
的字段一样?我尝试用 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));
}
}
上下文
我想在将文本文件的行传递给函数之前对其进行预处理 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
没有足够长的时间被 return 从迭代器的 next
方法中编辑出来。一种选择是 return 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
)。
我试图用类型参数 T
:
&str
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
在我看来,line
和 repl
的寿命足以被 f
处理,就像 &str
版本一样。至于上面的迭代器示例,使用 repl.to_string()
可以满足编译器的要求,但我不想克隆每一行。
我的直觉是问题出在 PhantomData<T>
上,我必须将它添加到我的结构中以满足编译器的要求。它是否限制 T
的生命周期(与包含的结构一样长),就像我有一个类型为 T
的字段一样?我尝试用 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));
}
}