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
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.
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.
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.
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 Some
s.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With