2016-12-07 42 views
6

Ten kod działa:Dlaczego Rust nie może wywnioskować wynikowego typu Iterator :: sum?

fn main() { 
    let a: i32 = (1i32..10).sum(); 
    let b = a.pow(2); 
} 

Jeśli usunąć typ i32 z a, następnie uzyskać ten błąd:

rustc 1.13.0 (2c6933acc 2016-11-07) 
error: the type of this value must be known in this context 
--> <anon>:3:13 
    | 
5 |  let b = a.pow(2); 
    |    ^^^^^^^^ 

Run the example

Liczyłam, że Rust okazuje (1i32..10) w produkt i32 iterator, a następnie sum() wie, aby zwrócić i32. czego mi brakuje?

+0

Powiązane pytanie: http: // stackoverflow.com/q/40243061/1233251 –

Odpowiedz

6

Sposób sum jest zdefiniowane, to faktycznie niejednoznaczne; więcej niż jeden typ może implementować Sum<i32>. Oto przykład, gdzie stosowane są różne rodzaje dla a, z których oba skompilować:

#[derive(Clone,Copy)] 
struct Summer { 
    s: isize, 
} 

impl Summer { 
    fn pow(&self, p: isize) { 
     println!("pow({})", p); 
    } 
} 

impl std::iter::Sum<i32> for Summer { 
    fn sum<I>(iter: I) -> Self 
     where I: Iterator<Item = i32> 
    { 
     let mut result = 0isize; 
     for v in iter { 
      result += v as isize; 
     } 
     Summer { s: result } 
    } 
} 

fn main() { 
    let a1: i32 = (1i32..10).sum(); 
    let a2: Summer = (1i32..10).sum(); 
    let b1 = a1.pow(2); 
    let b2 = a2.pow(2); 
} 

Playground

Ponieważ oba są możliwe, wyraźnie typ nie można wywnioskować.

5

and then sum() knows to return an i32

To tutaj popełniłeś błąd. Sprawdź Iterator::sum:

fn sum<S>(self) -> S 
    where S: Sum<Self::Item> 

Zwraca ogólny typ S który musi realizować Sum. Snie musi musi pasować do Self::Item. Dlatego kompilator wymaga określenia typu, do którego należy dodać sumę.

Dlaczego ta funkcja jest przydatna? Sprawdź te dwie przykładowe implementacje ze standardowej biblioteki:

impl Sum<i8> for i8 
impl<'a> Sum<&'a i8> for i8 

Zgadza się! Można podsumować iterator z u8lub iteratora z &u8! Jeśli nie mamy tego, czym ten kod nie zadziała:

fn main() { 
    let a: i32 = (0..5).sum(); 
    let b: i32 = [0, 1, 2, 3, 4].iter().sum(); 
    assert_eq!(a, b); 
} 

As bluss points out, możemy to osiągnąć przez mającą skojarzony typ które mogłyby związać u8 -> u8 i &'a u8 -> u8.

Gdybyśmy mieli tylko typ powiązany, to docelowy typ sumy byłby zawsze stały, a stracilibyśmy elastyczność. Na przykład możemy również wdrożyć Sum dla naszych własnych typów.

W tym miejscu podsumowujemy u8 s, ale zwiększamy rozmiar typu, który sumujemy, ponieważ prawdopodobnie suma przekroczyłaby u8. Ta realizacja jest w Ponadto do istniejących wdrożeń z biblioteki standardowej:

#[derive(Debug, Copy, Clone)] 
struct Points(i32); 

impl std::iter::Sum<u8> for Points { 
    fn sum<I>(iter: I) -> Points 
     where I: Iterator<Item = u8> 
    { 
     let mut pts = Points(0); 
     for v in iter { 
      pts.0 += v as i32; 
     } 
     pts 
    } 
} 

fn main() { 
    let total: Points = (0u8..42u8).sum(); 
    println!("{:?}", total); 
} 
+1

Brakujący logiczny krok Myślę, że; To, że wiele typów chce zsumować to samo, nie oznacza, że ​​musi istnieć inny parametr. Można go rozwiązać za pomocą skojarzonego typu '& 'a i8 -> i8' i' i8 -> i8' i tak dalej. – bluss

+0

@bluss bardzo dobry punkt! – Shepmaster

+0

Dzięki. Mam na myśli, że to nie jest twój problem, to jest w bibliotece, ale ponieważ ta odpowiedź była pewnego rodzaju krokami do racjonalizacji, chciałem to wskazać. Racjonalizacja istniejącego impasu jest prawdopodobnie drogą do szczęścia ;-) – bluss