2016-06-03 12 views
4

Ich möchte Iterator::find verwenden, um auf libusb::Devices Objekt, das eine Signatur wie so hat:Mutate Artikel in Iterator :: finden

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

Ich möchte ein Gerät mit einem bestimmten vendor_id zu finden, die Device::device_descriptor Aufruf erfordert auf jedes Gerät. Das device_descriptor-Verfahren erfordert jedoch ein &mut für jedes Gerät, und das Suchverfahren gibt nur einen &.

Bedeutet dies, dass es nicht möglich ist, änderbare Methoden für eine der Methoden des Iterators zu verwenden (find, filter, etc.)?

Hier ist das Beispiel versuche ich zum Laufen zu bringen:

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

Hier wird der Fehler Ich erhalte:

error: cannot borrow immutable borrowed content `*dev` as mutable 

Antwort

5

Dies bedeutet, dass es unmöglich ist, wandelbar Methoden zu verwenden, auf irgendeiner der Methoden des Iterators (find, filter, etc.)?

In den Methoden, die einen Parameter vom Typ F: Fn*(&Self::Item) erhalten, ja. Man kann keine Methode aufrufen, die eine veränderbare Referenz (&mut) auf eine Referenz (&) erwartet. Zum Beispiel:

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 

Beachten Sie, dass diese Regel auch für die automatische Deref gilt.

Einige Methoden der Iterator erhalten einen Parameter vom Typ F: Fn*(Self::Item), wie map, filter_map usw. Diese Methoden Funktionen ermöglichen, die das Element mutieren.


Eine interessante Frage ist: Warum haben manche Methoden erwarten Fn*(&Self::Item) und andere Fn*(Self::item)?

Die Methoden, die das Element, wie filter (das wird das Einzelteil, wenn die Filter-Funktion gibt true) verwenden müssen, nicht Self::Item als Parameter an die Funktion übergeben kann, da dadurch, dass das Eigentum an dem Punkt bedeutet geben die Funktion. Aus diesem Grund übergeben Methoden wie filter&Self::Item, so dass sie das Element später verwenden können.

Auf der anderen Seite, Methoden wie map und filter_map brauchen das Element nicht, nachdem sie als Argument verwendet werden (die Elemente, nachdem all abgebildet werden), so dass sie passieren das Element als Self::Item.


Im Allgemeinen ist es möglich, filter_map zu verwenden, um die Verwendung von filter in Fällen zu ersetzen, dass die Elemente mutiert werden müssen. In Ihrem Fall, können Sie dies tun:

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

Die filter_map filtert None Werte und erzeugt die Werte eingewickelt in Some s.

+0

Danke für die tolle Antwort. Eine andere Option, die in einigen Situationen funktionieren könnte, ist iter_mut(), aber libusb implementiert sie leider nicht. –

+1

Im Allgemeinen erzeugt 'iter_mut'' Item = & mut T', also '& Item = && mut T'. Das bedeutet, dass die Filterfunktion das Element nicht mutieren kann ... Siehe https://play.rust-lang.org/?gist=f01e4c90f80a777c10f01e217e29f739&version=stable&backtrace=0 – malbarbo