ฉันพบว่ามีประโยชน์ที่จะให้คอมไพเลอร์แนะนำฉัน:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
การรวบรวมให้:
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ทำตามคำแนะนำของคอมไพเลอร์และการคัดลอกวางที่เป็นประเภทการส่งคืนของฉัน (ด้วยการล้างข้อมูลเล็กน้อย):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
ปัญหาคือคุณไม่สามารถส่งคืนลักษณะที่เหมือนได้Iterator
เนื่องจากลักษณะไม่มีขนาด นั่นหมายความว่า Rust ไม่รู้ว่าจะจัดสรรพื้นที่สำหรับประเภทเท่าไร คุณไม่สามารถส่งคืนการอ้างอิงไปยังตัวแปรโลคัลได้ดังนั้นการส่งคืน&dyn Iterator
จึงไม่ใช่การเริ่มต้น
ลักษณะเด่น
ตั้งแต่ Rust 1.26 คุณสามารถใช้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());
}
มีข้อ จำกัด ในการใช้งาน คุณสามารถส่งคืนได้เพียงประเภทเดียวเท่านั้น (ไม่มีเงื่อนไข!) และต้องใช้กับฟังก์ชันฟรีหรือการใช้งานโดยธรรมชาติ
บรรจุกล่อง
หากคุณไม่รังเกียจที่จะสูญเสียประสิทธิภาพเล็กน้อยคุณสามารถคืนค่า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());
}
นี่เป็นตัวเลือกหลักที่ช่วยให้สามารถจัดส่งแบบไดนามิกได้ นั่นคือการใช้งานโค้ดที่แน่นอนจะถูกตัดสินในขณะทำงานแทนที่จะเป็นเวลาคอมไพล์ นั่นหมายความว่าสิ่งนี้เหมาะสำหรับกรณีที่คุณต้องการส่งคืนเครื่องย้ำคอนกรีตมากกว่าหนึ่งประเภทตามเงื่อนไข
ประเภทใหม่
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());
}
พิมพ์นามแฝง
ในฐานะที่เป็นออกแหลมโดย 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());
}
การจัดการกับการปิด
เมื่อimpl Trait
ไม่พร้อมใช้งานการปิดทำให้สิ่งต่างๆซับซ้อนมากขึ้น การปิดจะสร้างประเภทที่ไม่ระบุตัวตนและไม่สามารถตั้งชื่อในประเภทการส่งคืน:
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]>`
ในบางกรณีการปิดเหล่านี้สามารถแทนที่ด้วยฟังก์ชันซึ่งสามารถตั้งชื่อได้:
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>`
และปฏิบัติตามคำแนะนำข้างต้น:
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)
}
การจัดการกับเงื่อนไข
หากคุณต้องการเลือกตัววนซ้ำตามเงื่อนไขโปรดดูที่การทำซ้ำตามเงื่อนไขมากกว่าหนึ่งในตัวทำซ้ำที่เป็นไปได้หลายตัว