【问题标题】:How to implement a trait for any sequence of elements?如何为任何元素序列实现特征?
【发布时间】:2019-03-13 17:51:36
【问题描述】:

我正在尝试为任何元素序列实现一个特征,以便它适用于向量、数组和切片。到目前为止,我已经尝试了几种方法,但我无法编译它们中的任何一种:(

我有这个 trait,一个使用它的函数,以及一个实现这个 trait 的基本数据类型:

trait Hitable {
    fn hit(&self, val: f64) -> bool;
}

fn check_hit<T: Hitable>(world: &T) -> bool {
    world.hit(1.0)
}

struct Obj(f64);

impl Hitable for Obj {
    fn hit(&self, val: f64) -> bool {
        self.0 > val
    }
}

我希望能够为Obj 的序列实现该特征。 如果我将其限制为向量,它就可以正常工作:

impl<T> Hitable for Vec<T>
where
    T: Hitable,
{
    fn hit(&self, val: f64) -> bool {
        self.iter().any(|h| h.hit(val))
    }
}

fn main() {
    let v = vec![Obj(2.0), Obj(3.0)];
    println!("{}", check_hit(&v));
}

但我想让它更通用,以便它适用于数组和切片;我该怎么做?

我尝试了以下四种尝试:

尝试 #1:用于 Hitables 上的迭代器。

// It's not clear how to call it:
//    vec.iter().hit(...) does not compile
//    vec.into_iter().hit(...) does not compile
//
impl<T, U> Hitable for T
where
    T: Iterator<Item = U>,
    U: Hitable,
{
    fn hit(&self, val: f64) -> bool {
        self.any(|h| h.hit(val))
    }
}

尝试#2:对于可以变成迭代器的东西。

// Does not compile as well:
//
//         self.into_iter().any(|h| h.hit(val))
//         ^^^^ cannot move out of borrowed content
//
impl<T, U> Hitable for T
where
    T: IntoIterator<Item = U>,
    U: Hitable,
{
    fn hit(&self, val: f64) -> bool {
        self.into_iter().any(|h| h.hit(val))
    }
}

尝试 #3:切片。

// This usage doesn't compile:
//     let v = vec![Obj(2.0), Obj(3.0)];
//     println!("{}", check_hit(&v));
//
// It says that Hitable is not implemented for vectors.
// When I convert vector to slice, i.e. &v[..], complains about
// unknown size in compilation time.
impl<T> Hitable for [T]
where
    T: Hitable,
{
    fn hit(&self, val: f64) -> bool {
        self.iter().any(|h| h.hit(val))
    }
}

尝试 #4:迭代器 + 克隆

//     let v = vec![Obj(2.0), Obj(3.0)];
//     println!("{}", check_hit(&v.iter()));
//
// does not compile:
//     println!("{}", check_hit(&v.iter()));
//                    ^^^^^^^^^ `&Obj` is not an iterator
//
impl<T, U> Hitable for T
where
    T: Iterator<Item = U> + Clone,
    U: Hitable,
{
    fn hit(&self, val: f64) -> bool {
        self.clone().any(|h| h.hit(val))
    }
}

Playground link

【问题讨论】:

  • &amp;self — 你知道 Rust 中的可变性概念,以及 Iterator::next 需要可变接收器这一事实吗?
  • Hitable for T ... &amp;self — 你知道对类型的引用可能实现与类型本身不同的特征吗?
  • @Shepmaster 是的,我想我理解这些概念。我的 trait 不需要改变底层价值,所以也许在迭代器上实现它不是一个好主意。关于您的第二点:当我为某些引用类型 &T 实现特征时,我不太确定特征的函数参数 &self 会发生什么。会是&&T吗?我应该取消引用它吗?我在 trait 函数中使用 &self 来表明我只需要读取权限(没有所有权,没有突变)......
  • Playground ,这里是 into_iter() 的工作版本。和迭代器()。正如@Shepmaster 指出的那样,它与可变性有关。
  • @ÖmerErden 是的,谢谢,它现在确实有效。然而,现在当我意识到为迭代器实现它需要突变时,我认为最好只在切片上实现它,因为 trait 函数不应该需要突变(它只是读取数据)。

标签: generics rust iterator traits


【解决方案1】:

1。 Iterator-based

这行不通,因为迭代器需要可变才能推进它们,但您的 trait 需要 &amp;self

2。 IntoIterator-based

我会将特征更改为按值获取self,然后仅针对对Obj 的引用实现它。这也允许为任何实现IntoIterator的类型实现它:

trait Hitable {
    fn hit(self, val: f64) -> bool;
}

fn check_hit<T: Hitable>(world: T) -> bool {
    world.hit(1.0)
}

struct Obj(f64);

impl Hitable for &'_ Obj {
    fn hit(self, val: f64) -> bool {
        self.0 > val
    }
}

impl<I> Hitable for I
where
    I: IntoIterator,
    I::Item: Hitable,
{
    fn hit(self, val: f64) -> bool {
        self.into_iter().any(|h| h.hit(val))
    }
}

fn main() {
    let o = Obj(2.0);
    let v = vec![Obj(2.0), Obj(3.0)];

    println!("{}", check_hit(&o));
    println!("{}", check_hit(&v));
}

另见:

3。基于切片

我发现阅读整个错误消息,而不仅仅是一行摘要,会有所帮助:

error[E0277]: the size for values of type `[Obj]` cannot be known at compilation time
  --> src/main.rs:28:20
   |
28 |     println!("{}", check_hit(&v[..]));
   |                    ^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `[Obj]`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
note: required by `check_hit`
  --> src/main.rs:5:1
   |
5  | fn check_hit<T: Hitable>(world: &T) -> bool {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

具体来说,这一位:注意:check_hit 要求check_hit 要求 TSized。删除该限制允许此版本工作:

fn check_hit<T: Hitable + ?Sized>(world: &T) -> bool {
//                      ^~~~~~~~
    world.hit(1.0)
}

另见:

【讨论】:

  • 太好了,谢谢!是的,我已经看到了该错误消息,甚至检查了建议的链接。但是,我不明白我需要在哪里添加?Sized,出于某种原因,我认为我必须将它添加到特征实现的边界(我尝试过,但没有帮助)。这种方法(即在切片上实现特征)是否被认为是此类任务的最佳选择?
  • @dying_sphynx 我不知道“最佳”。我认为这两种可行的解决方案都是合理的。
猜你喜欢
  • 2021-11-14
  • 2015-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多