Tôi thấy hữu ích khi để trình biên dịch hướng dẫn tôi:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
Biên dịch mang lại:
error[E0308]: mismatched types
--> src/lib.rs:5:5
|
5 | text.split(' ')
| ^^^^^^^^^^^^^^^ expected (), found struct `std::str::Split`
|
= note: expected type `()`
found type `std::str::Split<'_, char>`
help: try adding a semicolon
|
5 | text.split(' ');
| ^
help: try adding a return type
|
3 | fn to_words(text: &str) -> std::str::Split<'_, char> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Thực hiện theo đề xuất của trình biên dịch và sao chép dán làm kiểu trả về của tôi (với một chút dọn dẹp):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
Vấn đề là bạn không thể trả về một đặc điểm như Iterator
vì một đặc điểm không có kích thước. Điều đó có nghĩa là Rust không biết có bao nhiêu không gian để phân bổ cho kiểu. Bạn cũng không thể trả về một tham chiếu đến một biến cục bộ , do đó, trả về &dyn Iterator
không phải là biến cục bộ.
Đặc điểm cấy ghép
Kể từ Rust 1.26, bạn có thể sử dụng impl trait
:
fn to_words<'a>(text: &'a str) -> impl Iterator<Item = &'a str> {
text.split(' ')
}
fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}
Có những hạn chế về cách thức này có thể được sử dụng. Bạn chỉ có thể trả về một kiểu duy nhất (không có điều kiện!) Và nó phải được sử dụng trên một hàm miễn phí hoặc một triển khai cố hữu.
Đóng hộp
Nếu bạn không ngại mất một chút hiệu quả, bạn có thể trả về Box<dyn Iterator>
:
fn to_words<'a>(text: &'a str) -> Box<dyn Iterator<Item = &'a str> + 'a> {
Box::new(text.split(' '))
}
fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}
Đây là tùy chọn chính cho phép điều phối động . Đó là, việc triển khai chính xác mã được quyết định tại thời điểm chạy, thay vì thời gian biên dịch. Điều đó có nghĩa là điều này phù hợp với các trường hợp bạn cần trả về nhiều hơn một loại trình lặp cụ thể dựa trên một điều kiện.
Loại mới
use std::str;
struct Wrapper<'a>(str::Split<'a, char>);
impl<'a> Iterator for Wrapper<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
fn to_words(text: &str) -> Wrapper<'_> {
Wrapper(text.split(' '))
}
fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}
Nhập bí danh
Như đã chỉ ra bởi reem
use std::str;
type MyIter<'a> = str::Split<'a, char>;
fn to_words(text: &str) -> MyIter<'_> {
text.split(' ')
}
fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}
Đối phó với việc đóng cửa
Khi impl Trait
không có sẵn để sử dụng, việc đóng cửa khiến mọi thứ trở nên phức tạp hơn. Đóng cửa tạo ra các loại ẩn danh và chúng không thể được đặt tên trong loại trả lại:
fn odd_numbers() -> () {
(0..100).filter(|&v| v % 2 != 0)
}
found type `std::iter::Filter<std::ops::Range<{integer}>, [closure@src/lib.rs:4:21: 4:36]>`
Trong một số trường hợp nhất định, các bao đóng này có thể được thay thế bằng các hàm, có thể được đặt tên là:
fn odd_numbers() -> () {
fn f(&v: &i32) -> bool {
v % 2 != 0
}
(0..100).filter(f as fn(v: &i32) -> bool)
}
found type `std::iter::Filter<std::ops::Range<i32>, for<'r> fn(&'r i32) -> bool>`
Và theo lời khuyên trên:
use std::{iter::Filter, ops::Range};
type Odds = Filter<Range<i32>, fn(&i32) -> bool>;
fn odd_numbers() -> Odds {
fn f(&v: &i32) -> bool {
v % 2 != 0
}
(0..100).filter(f as fn(v: &i32) -> bool)
}
Đối phó với các điều kiện
Nếu bạn cần chọn một trình lặp có điều kiện, hãy tham khảo Lặp có điều kiện qua một trong một số trình lặp có thể .