Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mutate the item in Iterator::find's closure?

Tags:

rust

libusb

I would like to use Iterator::find on libusb::Devices object, which has a signature like so:

fn find<P>(&mut self, predicate: P) -> Option<Self::Item> 
    where Self: Sized, P: FnMut(&Self::Item) -> bool

I want to find a device with a particular vendor_id, which requires calling Device::device_descriptor on each device. However, the device_descriptor method requires a &mut to each device, and the find method only gives a &.

Does this mean that it's impossible to use mutable methods on any of the Iterator's methods (find, filter, etc.)?

Here's the example I'm trying to get working:

let mut device = context
    .devices()
    .unwrap()
    .iter()
    .find(&mut |dev: &libusb::Device| { 
         dev.device_descriptor().unwrap().vendor_id() == vendor_id
     })
    .unwrap();

Here is the error I'm getting:

error: cannot borrow immutable borrowed content `*dev` as mutable
like image 424
Jason Martens Avatar asked Jun 03 '16 17:06

Jason Martens


People also ask

What does collect () do in Rust?

collect() can take all the values in an Iterator 's stream and stick them into a Vec . And the map method is now generating Result<i32, &str> values, so everything lines up.

What does ITER do in Rust?

An iterator helps to iterate over a collection of values such as arrays, vectors, maps, etc. Iterators implement the Iterator trait that is defined in the Rust standard library. The iter() method returns an iterator object of the collection. Values in an iterator object are called items.

How do you know if an iterator is empty in Rust?

You can make your iterator peekable and peek the first item; if it's None , then the iterator is empty. peek doesn't consume the item1, so the next call to next will return it.


2 Answers

Does this mean that it's impossible to use mutable methods on any of the Iterator's methods (find, filter, etc.)?

In the methods that receive a parameter of type F: Fn*(&Self::Item), yes. One cannot call a method that expects a mutable reference (&mut) on a reference (&). For example:

let mut x = vec![10];
// (&x)[0] = 20; // not ok
(&mut x)[0] = 20; // ok

//(& (&x))[0] = 20; // not ok 
//(& (&mut x))[0] = 20; // not ok
(&mut (&mut x))[0] = 20; // ok

Note that this rule also applies to auto deref.

Some methods of Iterator receive a parameter of type F: Fn*(Self::Item), like map, filter_map, etc. These methods allow functions that mutate the item.


One interesting question is: Why do some methods expect Fn*(&Self::Item) and others Fn*(Self::item)?

The methods that will need to use the item, like filter (that will return the item if the filter function returns true), cannot pass Self::Item as parameter to the function, because doing that means give the ownership of the item to the function. For this reason, methods like filter pass &Self::Item, so they can use the item later.

On the other hand, methods like map and filter_map do not need the item after they are used as arguments (the items are being mapped after all), so they pass the item as Self::Item.


In general, it is possible to use filter_map to replace the use of filter in cases that the items need to be mutated. In your case, you can do this:

extern crate libusb;

fn main() {
    let mut context = libusb::Context::new().expect("context creation");

    let mut filtered: Vec<_> = context.devices()
        .expect("devices list")
        .iter()
        .filter_map(|mut r| {
            if let Ok(d) = r.device_descriptor() {
                if d.vendor_id() == 7531 {
                    return Some(r);
                }
            }
            None
        })
        .collect();

    for d in &mut filtered {
        // same as: for d in filtered.iter_mut()
        println!("{:?}", d.device_descriptor());
    }
}

The filter_map filters out None values and produces the wrapped values in Somes.

like image 95
malbarbo Avatar answered Sep 22 '22 14:09

malbarbo


Answer migrated from: How can I apply mutating method calls to each struct when using Iter::find?

You can use Iterator::find_map instead, whose closure takes in elements by value which can then be easily mutated:

games
    .iter_mut()
    .find_map(|game| {
        let play = rng.gen_range(1..=10);
        game.play(play).then(|| game)
    })

Playground

like image 41
EvilTak Avatar answered Sep 23 '22 14:09

EvilTak