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. 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:
// 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))
}
}
// 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))
}
}
// 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))
}
}
// 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))
}
}
Iterator
-based This cannot work because iterators need to be mutable in order to advance them, but your trait requires &self
.
IntoIterator
-based I'd change the trait to take self
by value and then only implement it for references to Obj
. This also allows implementing it for any type that implements 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:
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_hit
— check_hit
requires that T
be Sized
. Removing that restriction allows this version to work:
fn check_hit<T: Hitable + ?Sized>(world: &T) -> bool {
// ^~~~~~~~
world.hit(1.0)
}
See also:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.