赞
踩
use std::io::{self, Write}; fn main() { loop { print!("> "); io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let result = eval(&input); println!("{}", result); } } fn eval(input: &str) -> i32 { let mut tokens = input.trim().split(' '); let mut result = tokens.next().unwrap().parse().unwrap(); while let Some(operator) = tokens.next() { let operand = tokens.next().unwrap().parse().unwrap(); match operator { "+" => result += operand, "-" => result -= operand, "*" => result *= operand, "/" => result /= operand, _ => panic!("Invalid operator"), } } result }
unwrap
的作用,请使用中文回复回答:
unwrap()函数用于从Result<T, E>中获取T,如果Result是Err,则会引发panic。在这种情况下,如果flush()失败,程序将崩溃并打印错误消息。
enum Result<T, E> { Ok(T), Err(E), } #[derive(Debug, PartialEq)] enum EvalError { InvalidOperator, ParseError, } fn eval(input: &str) -> Result<i32, EvalError> { let mut tokens = input.trim().split(' '); let mut result = tokens.next().ok_or(EvalError::ParseError)?.parse().map_err(|_| EvalError::ParseError)?; while let Some(operator) = tokens.next() { let operand = tokens.next().ok_or(EvalError::ParseError)?.parse().map_err(|_| EvalError::ParseError)?; match operator { "+" => result += operand, "-" => result -= operand, "*" => result *= operand, "/" => result /= operand, // 在这里,如果运算符不是+,-,*或/,我们将返回一个Err(EvalError::InvalidOperator)。这将使调用eval()的代码知道发生了什么,并且可以采取适当的措施。 _ => return Err(EvalError::InvalidOperator), } } // Ok是一个枚举类型Result<T, E>的变体,表示操作成功并包含结果。在这种情况下,我们返回一个包含结果的Ok变体,该结果是eval()函数的计算结果。 Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_eval() { assert_eq!(eval("1 + 2"), Ok(3)); assert_eq!(eval("2 * 3"), Ok(6)); assert_eq!(eval("4 / 2"), Ok(2)); assert_eq!(eval("5 - 3"), Ok(2)); assert_eq!(eval("1 + 2 * 3"), Ok(7)); assert_eq!(eval("1 +"), Err(EvalError::ParseError)); assert_eq!(eval("1 + a"), Err(EvalError::ParseError)); assert_eq!(eval("1 & 2"), Err(EvalError::InvalidOperator)); } }
let mut input = String::new();
在Rust中,mut
关键字用于声明可变变量。在上面的代码中,mut
关键字用于声明一个可变的字符串变量input
,这个变量将用于存储从标准输入读取的用户输入。由于用户输入可能会改变,因此需要使用mut
关键字来声明这个变量是可变的。
const MY_CONST_STRING: &str = “Hello, world!”;
let my_string = String::from(“This is a string variable.”);
String::new()创建一个空的String对象,而String::from()可以从一个字符串字面量或其他字符串类型创建一个String对象。
// & 表示这是一个字符串 slice,它是对一个字符串的引用,而不是拥有它。这意味着它不会拥有它所指向的字符串的所有权,因此它不会在离开作用域时释放它。
const MY_CONST_STRING: &str = “Hello, world!”;
let my_string = String::from(“This is a string variable.”);
fn main() { let s = String::from("hello"); // s进入作用域 takes_ownership(s); // s的值移动到函数里 ... // ... 所以到这里不再有效 let x = 5; // x进入作用域 makes_copy(x); // x 应该移动函数里, // 但i32是Copy的,所以在后面可继续使用x } // 这里,x先离开了作用域,然后是s。但因为s的值已被移走, // 所以不会有特殊操作 fn takes_ownership(some_string: String) { // some_string进入作用域 println!("{}", some_string); } // 这里,some_string离开作用域并调用`drop`方法。占用的内存被释放 fn makes_copy(some_integer: i32) { // some_integer进入作用域 println!("{}", some_integer); } // 这里,some_integer离开作用域。不会有特殊操作
fn main() {
let s = String::from("hello"); // s进入作用域
takes_reference(&s); // 传递s的引用,s的所有权不会被转移
println!("{}", s); // s仍然有效
}
fn takes_reference(some_string: &String) { // some_string是一个对String的引用
println!("{}", some_string);
} // some_string离开作用域,但是因为它只是对值的引用,所以不会有特殊操作
std::str::SplitWhitespace<'_>
是的,tokens.next() 的 next 是类型 std::str::SplitWhitespace<'_> 的方法。它的作用是返回迭代器中的下一个元素。在这个例子中,tokens 是一个字符串分割迭代器,使用空格作为分隔符。tokens.next() 返回迭代器中的第一个元素,即第一个数字。接下来,while 循环使用 tokens.next() 获取操作符和操作数。
a 的类型是 Option<&str>
enum Option<T> {
Some(T),
None,
}
Option<T>
的实现如上所示。它是一个枚举类型,有两个变体:Some(T)
和 None
。Some(T)
变体持有类型为 T
的值,而 None
变体表示缺少值。
下面是一个简短的使用 Option<T>
的例子:
fn divide(x: i32, y: i32) -> Option<i32> { if y == 0 { None } else { Some(x / y) } } fn main() { let result1 = divide(10, 2); let result2 = divide(10, 0); match result1 { Some(x) => println!("10 / 2 = {}", x), None => println!("Cannot divide by zero!"), } match result2 { Some(x) => println!("10 / 0 = {}", x), None => println!("Cannot divide by zero!"), } }
在这个例子中,divide
函数返回的是 Option<i32>
类型而不是普通的 i32
类型。如果分母为零,它返回 None
表示除法未定义。否则,它返回 Some(x / y)
表示除法的结果。
在 main
函数中,我们使用不同的参数两次调用 divide
函数。然后,我们使用模式匹配从每个 Option<i32>
值中提取结果。如果值是 Some(x)
,我们打印结果。如果值是 None
,我们打印一个错误消息。
match
是 Rust 中的一个关键字,用于模式匹配。它可以与 Option<T>
类型一起使用,以处理可能缺少值的情况。
例如,假设我们有一个 Option<i32>
类型的变量 x
,我们可以使用 match
来检查它是否包含一个值:
match x {
Some(n) => println!("The value is {}", n),
None => println!("There is no value."),
}
在这个例子中,如果 x
包含一个值,我们将打印该值。否则,我们将打印一条消息表示缺少值。
match
还可以与 if let
一起使用,以更简洁地处理只关心某个特定值的情况。例如,假设我们只关心 x
是否等于 Some(42)
,我们可以这样写:
if let Some(42) = x {
println!("The value is 42!");
}
在这个例子中,如果 x
等于 Some(42)
,我们将打印一条消息。否则,什么也不会发生。
是的,match
可以与其他用户自定义的类型一起使用,以处理不同的情况。例如,假设我们有一个枚举类型 Direction
,它表示一个方向:
enum Direction { Up, Down, Left, Right, } fn main() { let d = Direction::Left; match d { Direction::Up => println!("Going up!"), Direction::Down => println!("Going down!"), Direction::Left => println!("Going left!"), Direction::Right => println!("Going right!"), } }
在这个例子中,我们定义了一个枚举类型 Direction
,它有四个变体:Up
、Down
、Left
和 Right
。然后,我们使用 match
来处理不同的情况。如果 d
是 Direction::Up
,我们将打印一条消息表示向上移动。如果 d
是 Direction::Down
,我们将打印一条消息表示向下移动。如果 d
是 Direction::Left
,我们将打印一条消息表示向左移动。如果 d
是 Direction::Right
,我们将打印一条消息表示向右移动。
这个例子展示了如何使用 match
处理用户自定义的类型。在实际编程中,我们可以使用 match
处理各种不同的情况,以便编写更健壮的代码。
let my_string = String::from("hello");
match my_string.as_str() {
"hello" => println!("Found hello!"),
"world" => println!("Found world!"),
_ => println!("Found something else!"),
}
_ 的作用是匹配所有其他情况,类似于 switch 语句中的 default 分支。如果没有其他分支匹配,就会执行 _ 分支。
声明一个变量 operator 并将其绑定到 tokens.next() 的返回值,如果返回值是 Some,则执行循环体。这里的 let 是一个模式匹配语句,它将 Some 中的值绑定到 operator 变量中。如果返回值是 None,则退出循环。
将字符串解析为相应的类型,例如将字符串解析为整数或浮点数。
use std::io::{self, Write}; fn main() { loop { print!("> "); io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let result = eval(&input); println!("{}", result); } } fn eval(input: &str) -> i32 { let mut tokens = input.trim().split(' '); let mut result = tokens.next().unwrap().parse().unwrap(); while let Some(operator) = tokens.next() { let operand = tokens.next().unwrap().parse().unwrap(); match operator { "+" => result += operand, "-" => result -= operand, "*" => result *= operand, "/" if operand == 0 => panic!("Cannot divide by zero!"), // 如果操作数为零,则除法未定义,因此抛出一个 panic。 "/" => result /= operand, // 否则,执行除法运算。 _ => panic!("Invalid operator"), } } result }
第一个 unwrap()
用于从迭代器 tokens
中提取下一个标记。由于 tokens
是 &str
的迭代器,我们需要使用 parse()
方法将字符串解析为适当的类型。第二个 unwrap()
用于从 parse()
返回的 Result
类型中提取解析后的值。如果解析失败,unwrap()
将会 panic 并终止程序。
#[test]
fn test_eval() {
assert_eq!(eval("1 + 2"), 3);
assert_eq!(eval("10 - 3"), 7);
assert_eq!(eval("2 * 3"), 6);
assert_eq!(eval("10 / 2"), 5);
assert_eq!(eval("10 / 3"), 3);
assert_eq!(eval("1 + 2 * 3"), 7);
assert_eq!(eval("(1 + 2) * 3"), 9);
assert_eq!(eval("1 / 0"), panic!("Cannot divide by zero!"));
assert_eq!(eval("1 +"), panic!("Invalid operator"));
}
#[test]
是 Rust 中的一个属性,用于标记测试函数。测试函数是一些用于测试代码的函数,它们通常包含一些断言,用于检查代码是否按预期工作。
例如,假设我们有一个名为 add
的函数,它将两个数字相加并返回结果。我们可以编写一个测试函数来测试它是否按预期工作:
fn add(x: i32, y: i32) -> i32 {
x + y
}
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
assert_eq!(add(5, 7), 12);
assert_eq!(add(-3, 3), 0);
}
在这个例子中,我们定义了一个函数 add
,它将两个数字相加并返回结果。然后,我们使用 #[test]
属性定义了一个测试函数 test_add
,它包含三个断言,用于检查 add
函数是否按预期工作。如果所有断言都通过,测试函数将通过。否则,测试函数将失败,并显示相应的错误消息。
在 Rust 中,测试函数通常包含在与源代码相同的文件中,并使用 #[cfg(test)]
属性标记。这个属性告诉 Rust 只在运行测试时编译这些函数,而在构建可执行文件时忽略它们。这样可以确保测试代码不会影响生产代码的性能和可靠性。
#[cfg(test)]
mod tests {
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
assert_eq!(add(5, 7), 12);
assert_eq!(add(-3, 3), 0);
}
}
在这个例子中,我们使用 #[cfg(test)]
属性定义了一个测试模块 tests
,它包含一个测试函数 test_add
。这个模块只在运行测试时编译
- cursor-tutor/
- src/
- main.rs
- Cargo.toml
cargo build
cargo run
cargo test
Compiling cursor-tutor v0.1.0 (/home/user/cursor-tutor)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
> 99 / 3
33
running 1 test
test tests::test_eval ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
–end–
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。