Как реализовать черту для любой последовательности элементов? - PullRequest
0 голосов
/ 13 марта 2019

Я пытаюсь реализовать черту для любой последовательности элементов, чтобы она работала для векторов, массивов и слайсов.До сих пор я пробовал несколько подходов, но не могу скомпилировать ни один из них: (

У меня есть эта черта, и функция, которая ее использует, и базовый тип данных, реализующий эту черту:

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

ссылка на игровую площадку

1 Ответ

1 голос
/ 13 марта 2019

1.Iterator на основе

Это не может работать, потому что итераторы должны быть изменяемыми для их продвижения, но ваша черта требует &self.

2.IntoIterator на основе

Я бы изменил черту на значение 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 требует, чтобы T было Sized.Снятие этого ограничения позволяет этой версии работать:

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

См. Также:

...