2016-01-23 17 views
26

Tôi muốn viết một hàm chấp nhận một trình lặp và trả về kết quả của một số thao tác trên nó. Cụ thể, tôi đang cố gắng để lặp qua các giá trị của một HashMap:Làm thế nào để viết một hàm Rust có một trình lặp?

use std::collections::HashMap; 

fn find_min<'a>(vals: Iterator<Item=&'a u32>) -> Option<&'a u32> { 
    vals.min() 
} 

fn main() { 
    let mut map = HashMap::new(); 
    map.insert("zero", 0u32); 
    map.insert("one", 1u32); 
    println!("Min value {:?}", find_min(map.values())); 
} 

Nhưng than ôi:

error: the `min` method cannot be invoked on a trait object 
--> src/main.rs:4:10 
    | 
4 |  vals.min() 
    |   ^^^ 

error[E0277]: the trait bound `std::iter::Iterator<Item=&'a u32> + 'static: std::marker::Sized` is not satisfied 
--> src/main.rs:3:17 
    | 
3 | fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> { 
    |     ^^^^ `std::iter::Iterator<Item=&'a u32> + 'static` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=&'a u32> + 'static` 
    = note: all local variables must have a statically known size 

error[E0308]: mismatched types 
    --> src/main.rs:11:41 
    | 
11 |  println!("Min value {:?}", find_min(map.values())); 
    |           ^^^^^^^^^^^^ expected trait std::iter::Iterator, found struct `std::collections::hash_map::Values` 
    | 
    = note: expected type `std::iter::Iterator<Item=&u32> + 'static` 
       found type `std::collections::hash_map::Values<'_, &str, u32>` 

tôi nhận được lỗi tương tự nếu tôi cố gắng vượt qua bằng cách tham khảo; nếu tôi sử dụng Box, tôi sẽ gặp phải lỗi lâu dài.

Trả lời

21

Bạn muốn sử dụng Generics ở đây:

fn find_min<'a, I>(vals: I) -> Option<&'a u32> 
where 
    I: Iterator<Item = &'a u32>, 
{ 
    vals.min() 
} 

đặc điểm có thể được sử dụng theo hai cách: như giới hạn trên các thông số loại hình và đặc điểm như các đối tượng. Cuốn sách Ngôn ngữ lập trình gỉ có một chương trên traits và một chương trên trait objects giải thích hai trường hợp sử dụng này.

Ngoài ra, bạn thường muốn đi một cái gì đó mà thực hiện IntoIterator vì điều này có thể làm cho mã gọi hàm của bạn đẹp hơn:

fn find_min<'a, I>(vals: I) -> Option<&'a u32> 
where 
    I: IntoIterator<Item = &'a u32>, 
{ 
    vals.into_iter().min() 
} 
+0

Tôi đã rất thân thiết. Để giải thích, sự khác biệt với Generics là công văn tĩnh, tức là, Rust đang tạo ra một phiên bản của hàm này cho từng loại cụ thể mà tôi gọi nó là? –

+2

Chính xác. Bằng cách này, trình biên dịch biết loại của trình lặp, do đó nó biết kích thước của nó, do đó, nó biết bao nhiêu bộ nhớ cần thiết để dự trữ. Ngoài ra, một kiểu như 'Iterator ' không thể được sử dụng một mình; nó chỉ có thể được sử dụng sau một con trỏ. –

+6

Lưu ý rằng trong khi yêu cầu 'I' để thực hiện' Iterator' là hoàn toàn chính xác nếu bạn muốn chuyển các trình vòng lặp tùy ý vào hàm, thì cách chung chung hơn sẽ là yêu cầu 'I' thực hiện [' IntoIterator'] (http: // doc.rust-lang.org/std/iter/trait.IntoIterator.html). Nó cho phép bạn vượt qua vòng lặp quá nhưng bạn cũng sẽ có thể chuyển bất kỳ thứ gì có thể được * chuyển đổi * thành một trình lặp, mà không cần gọi các phương thức chuyển đổi một cách rõ ràng. Tôi muốn nói đây là * cách tiếp cận thành ngữ * để sử dụng các trình vòng lặp và các vòng lặp. –

4

Hành vi này là một chút unintuitive từ những người có một nền Python hơn, nói, một nền C++, vì vậy hãy để tôi làm rõ một chút.

Trong Rust, các giá trị được lưu trữ khái niệm bên trong tên liên kết chúng. Do đó, nếu bạn viết

let mut x = Foo { t: 10 }; 
let mut y = x; 
x.t = 999; 

y.t vẫn sẽ là 10.

Vì vậy, khi bạn viết

let x: Iterator<Item=&'a u32>; 

(hoặc tương tự trong danh sách tham số chức năng), Rust cần phải phân bổ đủ không gian cho bất kỳ giá trị loại Iterator<Item=&'a u32>. Ngay cả khi điều này là có thể, nó sẽ không hiệu quả.

Vậy điều gì Rust làm thay vào đó là cung cấp cho bạn tùy chọn để

  • Đặt giá trị trên heap, ví dụ. với Box, mang lại ngữ nghĩa kiểu Python. Sau đó, bạn có thể dùng chung với &mut Iterator<Item=&'a u32>.

  • Chuyên biệt hóa từng lời gọi chức năng cho từng loại có thể để thỏa mãn giới hạn. Điều này linh hoạt hơn, vì tham chiếu trait là một chuyên môn có thể, và cho trình biên dịch nhiều cơ hội hơn cho chuyên môn, nhưng có nghĩa là bạn không thể có công khai động (nơi loại có thể thay đổi phụ thuộc vào tham số thời gian chạy).

Các vấn đề liên quan