扩展库中的固有类型是不好的形式吗?
Is it bad form to extend inherent types in a library?
我有一个函数 downsample_vec
,它接受一个 Vec
并根据它们的位置删除它的一些值。我在注释正确的特征时遇到了麻烦(我只需要 Clone
和 Index
,但无法让它工作),所以我决定使用 self
看看我是否可以说服编译器做出正确的推论:
impl Vec<IndexMut<usize>> {
fn downsample<usize>(&mut self, factor: usize) {
let len = self.len();
if factor > len {
self.clear(); // downsample factor skips all elements
} else if factor == 1 {
return; // no actual downsampling
}
for ind in 0..len() {
if ind % factor != 0 {
self.remove(ind);
}
}
}
}
这给出了编译错误the value of the associated type `Output` (from the trait `std::ops::Index`) must be specified [E0191]
。
我不太确定如何指定输出类型,也没有找到任何关于如何执行此操作的参考,只是关于一般特征的博客。我的主要资源是 the rust vec .retain()
source.
首先在特定库中像这样在本地进行 monkeypatch Vec
是一种不好的形式,还是有正确的方法来做到这一点? (原来我写的单独函数在the playground可以查看)
您的代码有一些问题。
首先,您不能为您未在自己的包中定义的类型编写 impl
块。您能做的最好的事情就是定义一个新特征,然后为外部类型实现该特征。这是一种常见的做法,通常称为 "extension trait",使用命名方案 *Ext
,例如 MetadataExt
。所以我们可以这样构建它:
trait DownsampleExt {
fn downsample(&mut self, factor: usize);
}
现在我们必须为 Vec
实施它。在您的代码中,您使用 IndexMut
特性,就好像您要确保向量本身是可变索引的一样。但是由于 Vec
已经是一个具体类型(或者更确切地说是类型构造函数),编译器已经知道它是可变索引的。所以这已经有效了:
impl<T> DownsampleExt for Vec<T> {
fn downsample(&mut self, factor: usize) {
// action code
}
}
如果 DownsampleExt
特征在范围内,您可以在任何 Vec
对象上调用 downsample
。
但是,您的操作代码仍然存在一些bugs/has几个问题:
- 你说 "that takes a Vec and returns a copy of it with fewer values",但你给我们的代码改变了矢量而不是创建副本!请注意,您的描述与您的代码不符。
- 您没有检查
factor == 0
- 您的
for
循环不起作用:当我们在遍历索引时删除元素时,会使索引无效。每当您删除一个元素时,您都不能在该迭代中增加索引。
- 另请注意,您的算法在 O(n²) 内运行,因为
remove
是线性时间算法。这可能不是你想要的。
我有一个函数 downsample_vec
,它接受一个 Vec
并根据它们的位置删除它的一些值。我在注释正确的特征时遇到了麻烦(我只需要 Clone
和 Index
,但无法让它工作),所以我决定使用 self
看看我是否可以说服编译器做出正确的推论:
impl Vec<IndexMut<usize>> {
fn downsample<usize>(&mut self, factor: usize) {
let len = self.len();
if factor > len {
self.clear(); // downsample factor skips all elements
} else if factor == 1 {
return; // no actual downsampling
}
for ind in 0..len() {
if ind % factor != 0 {
self.remove(ind);
}
}
}
}
这给出了编译错误the value of the associated type `Output` (from the trait `std::ops::Index`) must be specified [E0191]
。
我不太确定如何指定输出类型,也没有找到任何关于如何执行此操作的参考,只是关于一般特征的博客。我的主要资源是 the rust vec .retain()
source.
首先在特定库中像这样在本地进行 monkeypatch Vec
是一种不好的形式,还是有正确的方法来做到这一点? (原来我写的单独函数在the playground可以查看)
您的代码有一些问题。
首先,您不能为您未在自己的包中定义的类型编写 impl
块。您能做的最好的事情就是定义一个新特征,然后为外部类型实现该特征。这是一种常见的做法,通常称为 "extension trait",使用命名方案 *Ext
,例如 MetadataExt
。所以我们可以这样构建它:
trait DownsampleExt {
fn downsample(&mut self, factor: usize);
}
现在我们必须为 Vec
实施它。在您的代码中,您使用 IndexMut
特性,就好像您要确保向量本身是可变索引的一样。但是由于 Vec
已经是一个具体类型(或者更确切地说是类型构造函数),编译器已经知道它是可变索引的。所以这已经有效了:
impl<T> DownsampleExt for Vec<T> {
fn downsample(&mut self, factor: usize) {
// action code
}
}
如果 DownsampleExt
特征在范围内,您可以在任何 Vec
对象上调用 downsample
。
但是,您的操作代码仍然存在一些bugs/has几个问题:
- 你说 "that takes a Vec and returns a copy of it with fewer values",但你给我们的代码改变了矢量而不是创建副本!请注意,您的描述与您的代码不符。
- 您没有检查
factor == 0
- 您的
for
循环不起作用:当我们在遍历索引时删除元素时,会使索引无效。每当您删除一个元素时,您都不能在该迭代中增加索引。 - 另请注意,您的算法在 O(n²) 内运行,因为
remove
是线性时间算法。这可能不是你想要的。