2015-06-17 6 views
5

Po prostu uczę się rdzy i pracuję nad zadaniem easy/r/dailyprogrammer. Oto niektóre kodu:Dlaczego nie mogę zniszczyć tej krotki podczas iteracji nad HashMap?

type ToDoList = HashMap<String, bool>; 

fn print(list: &ToDoList) { 
    let mut max_len: usize = 0; 
    for (item, _) in list.iter() { 
     max_len = max(max_len, item.len()); 
    } 
    let end = format!("+---{}-+", 
     iter::repeat("-").take(max_len).collect::<String>()); 

    println!("{}", end); 
    for (item, done) in list.iter() { 
     let line = format!("| {0} {1}{2} |", 
      if done {"☑"} else {"☐"}, 
      item, 
      iter::repeat("-") 
       .take(max_len - item.len()) 
       .collect::<String>() 
     ); 
     println!("{:?}", (item, done)); 
    } 
    println!("{}", end); 
} 

dostaję ten błąd z rustc:

error: type mismatch resolving `<std::collections::hash::map::Iter<'_, 
collections::string::String, bool> as core::iter::Iterator>::Item == 
(_, bool)`: expected &-ptr, 
    found bool [E0271] 
todolist.rs:19  for (item, done) in list.iter() { 
todolist.rs:20   let line = format!("| {0} {1}{2} |", 
todolist.rs:21    if done {"☑"} else {"☐"}, 
todolist.rs:22    item, 
todolist.rs:23    iter::repeat("-") 
todolist.rs:24     .take(max_len - item.len()) 
       ... 
todolist.rs:24:21: 24:31 error: the type of this value must be known in this context 
todolist.rs:24     .take(max_len - item.len()) 
               ^~~~~~~~~~ note: in expansion of format_args! <std macros>:2:26: 2:57 note: expansion site <std 
macros>:1:1: 2:61 note: in expansion of format! 
todolist.rs:20:14: 26:4 note: expansion site error: aborting due to 2 previous errors 

Wydaje się, że oba te związane są z tej samej kwestii, że jakoś wywołującego list.iter() próbuje dać mi krotki z (_, String, bool) zamiast tylko (String, bool). Dlaczego tak się dzieje?

Odpowiedz

7

Komunikat o błędzie jest mało czytelny. To, co się dzieje, polega na tym, że done jest typu &bool, ponieważ dokonujesz iteracji w sposób nie będący właścicielem.

type mismatch resolving <std::collections::hash::map::Iter<'_, collections::string::String, bool> as core::iter::Iterator>::Item == (_, bool) :

Zasadniczo należy sprawdzić, jaki jest rzeczywisty typ std::collections::hash::map::Iter::Item. Jak widać w dokumentach jest to (&'a K, &'a V).

Zmiana

for (item, done) in list.iter() { 

do

for (item, &done) in list.iter() { 

będzie rozwiązać problem.

Przyczyną tego zamieszania jest wnioskowanie typu Rusta. Ponieważ używasz done jako argumentu dla if, Rust wie, że musi być typu bool. Od tego momentu cofa się do przypisania - wiążącego, dopóki nie znajdzie innego konkretnego typu. W innych językach prawdopodobnie byłaby na odwrót, a błąd wystąpiłby w warunku if.


Jako marginesie, w swojej pierwszej iteracji for (item, _) in list.iter() { jesteś zainteresowany tylko w tonacjach z HashMap. Możesz użyć for item in list.keys() dla bardziej zwartej pętli.

2

It seems like both of these are related to the same issue that somehow calling list.iter() is trying to give me a tuple of (_, String, bool) instead of just (String, bool). Why is that happening?

Masz rację, że obie są związane z oryginalnym błędem, ale źle o błędzie:

(_, bool)`: expected &-ptr, 

Dostajesz (_, bool) krotki podczas gdy kompilator oczekiwać, aby zobaczyć krotki odniesień (&_, &bool) do czegoś . Ten _ może być String (lub &String) w komunikacie kompilatora, więc nie jest to problem.

Problem jest, że spodziewa się wartość gdzie kompilator spodziewa się odniesienia, wynika to z faktu, że iter() powraca referencje do fundamentów kolekcji iterować upon a prostym rozwiązaniem jest zmienić sposób dopasować:

for (item, &done) in &list { 
} 

jak widać w docs:

impl<'a, K, V> Iterator for Iter<'a, K, V> 
type Item = (&'a K, &'a V) 

Innym sposobem wokół byłoby wiązać done do &bool a następnie dereference nim przed użyciem:

for (item, done) in &list { 
    if *done { ... } else { ... } 
} 
Powiązane problemy