通用特征和生命周期的问题
Problem with generic traits and lifetimes
我在较大的上下文中遇到了通用特征的问题,并尝试将其缩小到这个较小的问题。我想要以下功能:
fn count<I, S, T>(pattern: T, item:I) -> usize
where
I: Atom,
S: Iterator<Item = I>
T: Atoms<I, S>,
{
pattern.atoms().filter(|i| i == &item).count()
}
该函数应传递两个参数:
pattern:Atoms<...>
有一个工厂方法 atoms returning 一个 atoms 的迭代器
item:Atom
这是atoms 的迭代器中应该计算的项目
该函数应该对两个参数都是通用的(约束条件是 pattern.atoms()
必须 return 一个迭代器,其项类型与 item
相同),例如:
count(Atoms<u8, std::str::Bytes<'_>>, u8) -> usize
count(Atoms<char, std::str::Chars<'_>>, char) -> usize
count(Atoms<u8, std::io::Bytes>, u8) -> usize
对于 Atom,我尝试了这种方法:
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
impl Atom for u8 {}
接下来我开始使用 Atoms,首先没有生命周期:
pub trait Atoms<I, S>
where
S: Iterator<Item = I>,
I: Atom,
{
fn atoms(&self) -> S;
}
我尝试使用 std::str::Bytes<'a>
作为迭代器为 str
实现这个。因此,编译器错过了原子中的生命周期注释 &'a self
。所以我用生命周期改进了这种方法并提出了这段代码:
pub trait Atoms<'a, I, S>
where
S: Iterator<Item = I> + 'a,
I: Atom,
{
fn atoms(&'a self) -> S;
}
impl<'a> Atoms<'a, u8, std::str::Bytes<'a>> for &str {
fn atoms(&'a self) -> std::str::Bytes<'a> {
self.bytes()
}
}
fn count<'a, I, S, T>(pattern: T, item: I) -> usize
where
I: Atom,
S: Iterator<Item = I> + 'a,
T: Atoms<'a, I, S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler complains
}
现在编译器报错 the parameter type
T may not live long enough, ... consider adding ... T:'a
。我不明白这个问题。该提示甚至不起作用(接下来是另一个终身问题)所以我不知道我的误解是什么。有人可以帮助我理解(甚至解决)这个问题吗?
atoms
函数需要self
被借用'a
的生命周期。如果你给这个函数一个活不了那么久的类型,这个函数就不能正常工作。
例如,类型 &'b T
的生命周期为 'b
。如果您为 &'b T
实现 Atoms
并且 'b
比 'a
更短,则您一定不能调用 atoms
函数。这就是为什么你必须限制 T
至少活到 'a
.
Rust 知道这一点并自动对 atoms
函数执行此操作:
fn atoms(&'a self) -> std::str::Chars<'a>
where
Self: 'a,
{ self.chars() }
但是当你试图在另一个函数中使用那个函数时,Rust 需要你自己添加这个键。这就是为什么您需要为 count
函数添加它。
第二个问题
您的 Atom
特质不需要承载 'a
生命周期。您的第一种方法是正确的。
pub trait Atoms<S>
where
S: Iterator,
S::Item: Atom,
{
fn atoms(&self) -> S;
}
(注意我去掉了泛型参数I
,换成了S::Item
。这是可能的,因为Item
是[=33的关联类型=]。这除了更清晰之外没有任何改变。)
在那之后,实现就非常简单了。
// Note the `&'a str` here, this was missing on your implementation but it is actually
// needed. If you think about it, the bytes that are returned are part of the `&str`
// so they must live at least as long as the reference.
impl<'a> Atoms<std::str::Bytes<'a>> for &'a str {
// The difference is here. You don't need to chose a lifetime for `&self`.
// A longer lifetime than `'a` would be ok. That would be a reference to
// a reference of a lifetime of `'a`.
//
// `self` is `&'a str` here.
// so `&self` is `&&'a str`
//
// What you did was telling Rust that that second reference needed to live
// as long as `'a`. Meaning `&'a &'a str`. But this was wrong. That reference
// must be able to live longer than `'a`.
fn atoms(&self) -> std::str::Bytes<'a> {
self.bytes()
}
}
在那之后,count
函数按预期工作:
fn count<S, T>(pattern: T, item: I) -> usize
where
S::Item: Atom,
S: Iterator,
T: Atoms<S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler does not complain anymore ;)
}
我在较大的上下文中遇到了通用特征的问题,并尝试将其缩小到这个较小的问题。我想要以下功能:
fn count<I, S, T>(pattern: T, item:I) -> usize
where
I: Atom,
S: Iterator<Item = I>
T: Atoms<I, S>,
{
pattern.atoms().filter(|i| i == &item).count()
}
该函数应传递两个参数:
pattern:Atoms<...>
有一个工厂方法 atoms returning 一个 atoms 的迭代器
item:Atom
这是atoms 的迭代器中应该计算的项目
该函数应该对两个参数都是通用的(约束条件是 pattern.atoms()
必须 return 一个迭代器,其项类型与 item
相同),例如:
count(Atoms<u8, std::str::Bytes<'_>>, u8) -> usize
count(Atoms<char, std::str::Chars<'_>>, char) -> usize
count(Atoms<u8, std::io::Bytes>, u8) -> usize
对于 Atom,我尝试了这种方法:
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
impl Atom for u8 {}
接下来我开始使用 Atoms,首先没有生命周期:
pub trait Atoms<I, S>
where
S: Iterator<Item = I>,
I: Atom,
{
fn atoms(&self) -> S;
}
我尝试使用 std::str::Bytes<'a>
作为迭代器为 str
实现这个。因此,编译器错过了原子中的生命周期注释 &'a self
。所以我用生命周期改进了这种方法并提出了这段代码:
pub trait Atoms<'a, I, S>
where
S: Iterator<Item = I> + 'a,
I: Atom,
{
fn atoms(&'a self) -> S;
}
impl<'a> Atoms<'a, u8, std::str::Bytes<'a>> for &str {
fn atoms(&'a self) -> std::str::Bytes<'a> {
self.bytes()
}
}
fn count<'a, I, S, T>(pattern: T, item: I) -> usize
where
I: Atom,
S: Iterator<Item = I> + 'a,
T: Atoms<'a, I, S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler complains
}
现在编译器报错 the parameter type
T may not live long enough, ... consider adding ... T:'a
。我不明白这个问题。该提示甚至不起作用(接下来是另一个终身问题)所以我不知道我的误解是什么。有人可以帮助我理解(甚至解决)这个问题吗?
atoms
函数需要self
被借用'a
的生命周期。如果你给这个函数一个活不了那么久的类型,这个函数就不能正常工作。
例如,类型 &'b T
的生命周期为 'b
。如果您为 &'b T
实现 Atoms
并且 'b
比 'a
更短,则您一定不能调用 atoms
函数。这就是为什么你必须限制 T
至少活到 'a
.
Rust 知道这一点并自动对 atoms
函数执行此操作:
fn atoms(&'a self) -> std::str::Chars<'a>
where
Self: 'a,
{ self.chars() }
但是当你试图在另一个函数中使用那个函数时,Rust 需要你自己添加这个键。这就是为什么您需要为 count
函数添加它。
第二个问题
您的 Atom
特质不需要承载 'a
生命周期。您的第一种方法是正确的。
pub trait Atoms<S>
where
S: Iterator,
S::Item: Atom,
{
fn atoms(&self) -> S;
}
(注意我去掉了泛型参数I
,换成了S::Item
。这是可能的,因为Item
是[=33的关联类型=]。这除了更清晰之外没有任何改变。)
在那之后,实现就非常简单了。
// Note the `&'a str` here, this was missing on your implementation but it is actually
// needed. If you think about it, the bytes that are returned are part of the `&str`
// so they must live at least as long as the reference.
impl<'a> Atoms<std::str::Bytes<'a>> for &'a str {
// The difference is here. You don't need to chose a lifetime for `&self`.
// A longer lifetime than `'a` would be ok. That would be a reference to
// a reference of a lifetime of `'a`.
//
// `self` is `&'a str` here.
// so `&self` is `&&'a str`
//
// What you did was telling Rust that that second reference needed to live
// as long as `'a`. Meaning `&'a &'a str`. But this was wrong. That reference
// must be able to live longer than `'a`.
fn atoms(&self) -> std::str::Bytes<'a> {
self.bytes()
}
}
在那之后,count
函数按预期工作:
fn count<S, T>(pattern: T, item: I) -> usize
where
S::Item: Atom,
S: Iterator,
T: Atoms<S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler does not complain anymore ;)
}