<-- Home |--rust

For Else Loop in Rust如何实现for-else语法

形而下之,下不为例

Rust中有没有for-else语法?

答案是,没有。

但是,我们可以通过一些方法来实现for-else语法。

各种实现方式

使用标志位

 1let mut found = false;
 2for i in 0..10 {
 3    if i == 15 {
 4        found = true;
 5        break;
 6    }
 7}
 8if !found {
 9    println!("Not found");
10}

这个思路非常简单,循环,如果没有找到,就执行else块。

if特殊用法

 1if 'search: loop {
 2    for i in 0..10 {
 3        if i == 15 {
 4            break 'search false;
 5        }
 6    }
 7    true
 8} {
 9    println!("Not found");
10}

这个是在耍赖,if后面是一个语句

1'search: loop {
2    for i in 0..10 {
3        if i == 15 {
4            break 'search false;
5        }
6    }
7    true
8} 

这个loop表达式返回一个布尔值,如果找到,返回false,否则返回true。

函数式

10..10.iter()
2    .find(|&i| i == 15)
3    .or_else(|| println!("Not found"));

这个是函数式的实现,使用find方法,如果找到,返回Some(i),否则返回None。这个归根结底使用了Option类型。

略微变态的if let

1if let None = (0..10).find(|&i| i == 15) {
2    println!("Not found");
3}

这个是略微变态的实现,使用if let语句,如果找到,返回Some(i)。后面还可以加个else块,如果找到了,就执行else块。

使用label语句块

1'label: {
2    for i in 0..10 {
3        if i == 15 {
4            break 'label false;
5        }
6    }
7    println!("Not found");
8}

这个是使用label语句块的实现,如果找到,就执行break语句,否则执行println!("Not found")语句。

说在最后

其实,Rust中没有for-else语法。上面这些都是一些奇技淫巧,用来实现for-else语法。没有任何意义,最好就是写点最平实无华的代码。

好吧,我们其实还能写一个宏来实现for-else语法。

 1//! A macro that provides a for-else control structure in Rust.
 2//! 
 3//! This macro allows you to execute code in an else block when a for loop completes without breaking.
 4//! It's similar to Python's for-else construct.
 5//! 
 6//! # Examples
 7//! 
 8//! ```rust
 9//! use forelse::for_else;
10//! 
11//! let mut found = false;
12//! for_else!(x in 1..=5 => {
13//!     if x == 3 {
14//!         println!("Found 3");
15//!         found = true;
16//!     }
17//! } else {
18//!     println!("Not found");
19//! });
20//! ```
21//! 
22//! The else block will execute if the loop completes without breaking.
23//! This is useful for search operations where you want to handle both found and not found cases.
24
25use quote::quote;
26use syn::{parse_macro_input, Expr, Block, Pat};
27
28/// The main macro that implements the for-else control structure.
29/// 
30/// # Syntax
31/// 
32/// ```rust
33/// use forelse::for_else;
34/// 
35/// for_else!(x in 1..=5 => {
36///     // loop body
37/// } else {
38///     // else body
39/// });
40/// ```
41/// 
42/// The else block will execute if the loop completes without breaking.
43#[proc_macro]
44pub fn for_else(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
45    let input = parse_macro_input!(input as ForElseInput);
46    
47    let pattern = input.pattern;
48    let range = input.range;
49    let body = input.body;
50    let else_body = input.else_body;
51    
52    let expanded = quote! {
53        {
54            let mut executed = false;
55            {
56                'for_else: for #pattern in #range {
57                    executed = true;
58                    #body
59                }
60            }
61            if !executed {
62                #else_body
63            }
64        }
65    };
66    
67    proc_macro::TokenStream::from(expanded)
68}
69
70/// Internal structure to parse the macro input
71struct ForElseInput {
72    pattern: Pat,
73    range: Expr,
74    body: Block,
75    else_body: Block,
76}
77
78impl syn::parse::Parse for ForElseInput {
79    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
80        let pattern = Pat::parse_single(input)?;
81        input.parse::<syn::Token![in]>()?;
82        let range = Expr::parse(input)?;
83        input.parse::<syn::Token![=>]>()?;
84        let body = Block::parse(input)?;
85        input.parse::<syn::Token![else]>()?;
86        let else_body = Block::parse(input)?;
87        
88        Ok(ForElseInput {
89            pattern,
90            range,
91            body,
92            else_body,
93        })
94    }
95}

测试的代码:

  1use forelse::for_else;
  2
  3/// Basic tests for the for-else macro
  4mod basic {
  5    use super::*;
  6
  7    /// Test finding an element in a range
  8    #[test]
  9    fn test_find_in_range() {
 10        let mut found = false;
 11        for_else!(x in 1..=5 => {
 12            if x == 3 {
 13                println!("Found 3");
 14                found = true;
 15            }
 16        } else {
 17            panic!("Should not reach else branch");
 18        });
 19        assert!(found);
 20    }
 21
 22    /// Test not finding an element in a range
 23    #[test]
 24    fn test_not_find_in_range() {
 25        let mut found = false;
 26        for_else!(x in 1..=5 => {
 27            if x == 6 {
 28                println!("Found 6");
 29                found = true;
 30            }
 31        } else {
 32            println!("Not found");
 33        });
 34        assert!(!found);
 35    }
 36
 37    /// Test with an empty range
 38    #[test]
 39    fn test_empty_range() {
 40        let mut found = false;
 41        for_else!(x in 1..1 => {
 42            println!("Found {}", x);
 43            found = true;
 44        } else {
 45            println!("Empty range");
 46        });
 47        assert!(!found);
 48    }
 49}
 50
 51/// Tests for control flow statements
 52mod control_flow {
 53    use super::*;
 54
 55    /// Test with break statement
 56    #[test]
 57    fn test_break() {
 58        let mut found = false;
 59        for_else!(x in 1..=5 => {
 60            if x == 3 {
 61                break;
 62            }
 63        } else {
 64            println!("Not found");
 65        });
 66        assert!(!found);
 67    }
 68
 69    /// Test with continue statement
 70    #[test]
 71    fn test_continue() {
 72        let mut found = false;
 73        for_else!(x in 1..=5 => {
 74            if x == 3 {
 75                continue;
 76            }
 77            found = true;
 78        } else {
 79            panic!("Should not reach else branch");
 80        });
 81        assert!(found);
 82    }
 83}
 84
 85/// Tests for different iterator types
 86mod iterators {
 87    use super::*;
 88
 89    /// Test with complex pattern matching
 90    #[test]
 91    fn test_tuple_iteration() {
 92        let mut found = false;
 93        for_else!((x, y) in [(1, 2), (3, 4), (5, 6)].iter() => {
 94            if x == &3 && y == &4 {
 95                println!("Found ({}, {})", x, y);
 96                found = true;
 97            }
 98        } else {
 99            panic!("Should not reach else branch");
100        });
101        assert!(found);
102    }
103
104    /// Test with string iteration
105    #[test]
106    fn test_string_iteration() {
107        let mut found = false;
108        for_else!(c in "hello".chars() => {
109            if c == 'e' {
110                println!("Found 'e'");
111                found = true;
112            }
113        } else {
114            panic!("Should not reach else branch");
115        });
116        assert!(found);
117    }
118}
119
120/// Tests for fallible types (Option and Result)
121mod fallible {
122    use super::*;
123
124    /// Test with Some variant
125    #[test]
126    fn test_some_iteration() {
127        let mut found = false;
128        for_else!(x in Some(42) => {
129            if x == 42 {
130                println!("Found 42");
131                found = true;
132            }
133        } else {
134            panic!("Should not reach else branch");
135        });
136        assert!(found);
137    }
138
139    /// Test with None variant
140    #[test]
141    fn test_none_iteration() {
142        let mut found = false;
143        for_else!(x in None::<i32> => {
144            println!("Found {}", x);
145            found = true;
146        } else {
147            println!("None case");
148        });
149        assert!(!found);
150    }
151
152    /// Test with Ok variant
153    #[test]
154    fn test_ok_iteration() {
155        let mut found = false;
156        for_else!(x in Ok::<i32, &str>(42) => {
157            if x == 42 {
158                println!("Found 42");
159                found = true;
160            }
161        } else {
162            panic!("Should not reach else branch");
163        });
164        assert!(found);
165    }
166
167    /// Test with Err variant
168    #[test]
169    fn test_err_iteration() {
170        let mut found = false;
171        for_else!(x in Err::<i32, &str>("error") => {
172            println!("Found {}", x);
173            found = true;
174        } else {
175            println!("Error case");
176        });
177        assert!(!found);
178    }
179} 

不知道这样行不行,当然,还是难看……

工程文件什么的:

结论

这还啥结论,Python程序员就不能问这样的问题,有且只有一种正确的方式编程是Python程序员的最大、唯一、不多的美德……


文章标签

|-->rust |-->for-else |-->loop |-->macro


GitHub