简体   繁体   English

如何为任何元素序列实现特征?

[英]How to implement a trait for any sequence of elements?

I am trying to implement a trait for any sequence of elements, so that it will work for vectors, arrays and slices. 我正在尝试为任何元素序列实现特征,以使其适用于向量,数组和切片。 So far I've tried several approaches, but I can't compile any of them :( 到目前为止,我已经尝试了几种方法,但是我不能编译它们中的任何一个:(

I have this trait, and a function that uses it, and a basic data type implementing the 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
    }
}

I'd like to be able to implement that trait for sequence of Obj 's. 我希望能够为Obj的序列实现该特征。 It works fine if I just restrict it to vectors: 如果我仅将其限制为向量,则它可以正常工作:

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));
}

But I want to make it more generic so that it works for arrays and slices; 但是我想使其更通用,以便它适用于数组和切片。 how can I do that? 我怎样才能做到这一点?

I tried the following four attempts: 我尝试了以下四个尝试:

Attempt #1: for iterator on Hitables. 尝试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))
    }
}

Attempt #2: for something which can be turned into iterator. 尝试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))
    }
}

Attempt #3: for slices. 尝试#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))
    }
}

Attempt #4: for Iterator + Clone 尝试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 游乐场链接

1. Iterator -based 1.基于Iterator

This cannot work because iterators need to be mutable in order to advance them, but your trait requires &self . 这是行不通的,因为迭代器需要可变才能提高它们,但是您的特质需要&self

2. IntoIterator -based 2.基于IntoIterator

I'd change the trait to take self by value and then only implement it for references to Obj . 我将特征更改为按值获取self ,然后仅实现对Obj引用。 This also allows implementing it for any type that implements IntoIterator : 这也允许为实现IntoIterator任何类型实现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));
}

See also: 也可以看看:

3. Slice-based 3.基于切片

I find that reading the entire error message, not just the one line summary, can help: 我发现阅读整个错误消息,而不仅仅是一行摘要,可以帮助您:

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 {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Specifically, this bit: note: required by check_hitcheck_hit requires that T be Sized . 具体来说,该位: 注意: check_hitcheck_hit要求T Sized Removing that restriction allows this version to work: 删除该限制将允许该版本工作:

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

See also: 也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM