Home > OS >  rust: Define a generic function that will accept a borrow to any iterable of things that implement a
rust: Define a generic function that will accept a borrow to any iterable of things that implement a

Time:07-16

I have a trait and a thing that implements that trait:

trait HasPosition2D {
    fn x(&self) -> &i32;
    fn y(&self) -> &i32;
    fn x_mut(&mut self) -> &mut i32;
    fn y_mut(&mut self) -> &mut i32;
}

impl HasPosition2D for (i32,i32) {
    fn x(&self) -> &i32 {&self.0}
    fn y(&self) -> &i32 {&self.1}
    fn x_mut(&mut self) -> &mut i32 {&mut self.0}
    fn y_mut(&mut self) -> &mut i32 {&mut self.1}
}

I can now define generic functions that accept anything that implements HasPosition2D. e.g

fn print(p: &impl HasPosition2D) {
    println!("{} {}", p.x(), p.y());
}

I have also defined a function that accepts anything that can be turned into an iterable of HasPosition2D. i.e

fn print_all(ps: impl IntoIterator<Item=impl HasPosition2D>) {
    for p in ps {
        println!("{} {}", p.x(), p.y());
    }
}

This allows me to swap the container type without having to change the code. e.g

let ps = vec![(42,42), (42,42)];
print_all(ps);
let ps = [(42,42), (42,42)];
print_all(ps);

However, the above function moves the container, I would instead like to borrow. e.g

//does not complile
fn print_all2(ps: &impl IntoIterator<Item=impl HasPosition2D>) {
    for p in ps {
        println!("{} {}", p.x(), p.y());
    }
}

But this gives the error:

error[E0277]: `&impl IntoIterator<Item = impl HasPosition2D>` is not an iterator
  --> src\main.rs:35:14
   |
35 |     for p in ps {
   |              ^^ `&impl IntoIterator<Item = impl HasPosition2D>` is not an iterator
   |
   = help: the trait `Iterator` is not implemented for `&impl IntoIterator<Item = impl HasPosition2D>`
   = help: the trait `Iterator` is implemented for `&mut I`
   = note: required because of the requirements on the impl of `IntoIterator` for `&impl IntoIterator<Item = impl HasPosition2D>`

I would also like to define a similar function that accepts a mutable borrow.

And finally, how could I create a type alias for each of these impl traits? ( using #![feature(type_alias_impl_trait)] and/or #![feature(trait_alias)] )

e.g, for the non borrowed version:

type HasPosition2DList = impl IntoIterator<Item=impl HasPosition2D>;

or

//does not compile
trait HasPosition2DList = IntoIterator<Item=impl HasPosition2D>;

How do I do this?

CodePudding user response:

into_iter takes self by value (not reference), so if you are wanting to avoid a move, then that won't fix your problem. Vec can deref into a slice, so I think you want something more like this, in order to accept either type as a paramter:

fn print_all(ps: &[impl HasPosition2D]) {
    for p in ps {
        println!("{} {}", p.x(), p.y());
    }
}

You can then call it as you showed in your example, just adding & to borrow:

fn main() {
    let ps = vec![(42,42), (42,42)];
    print_all(&ps);

    let ps = [(42,42), (42,42)];
    print_all(&ps);
}
  • Related