Rust - 输入输出
Rust - 基本输入与输出
Section titled “Rust - 基本输入与输出”本章节涵盖了与用户交互的基础知识:从标准输入(键盘)读取和写入标准输出(控制台)。我们还将了解如何处理命令行参数。
Read 和 Write 特性
Section titled “Read 和 Write 特性”Rust 的标准 I/O 库(std::io)围绕两个基本特性(trait)构建:用于输入的 Read 和用于输出的 Write。
Read:用于可从中读取字节的源,例如std::io::stdin()或File。Write:用于可向其写入字节的目标,例如std::io::stdout()或File。
从控制台读取
Section titled “从控制台读取”要从用户读取一行文本,你可以使用标准输入句柄上的 read_line 方法。此操作可能会失败(例如,由于操作系统级别的错误),因此它返回一个 Result 类型,你必须处理它。
use std::io;
fn main() { println!("Please enter your name:");
let mut name = String::new();
// 获取标准输入的句柄并读取一行。 // `read_line` 返回一个 `Result`,因此我们使用 `.expect()` 进行简单的错误处理。 io::stdin() .read_line(&mut name) .expect("Failed to read line");
// `read_line` 包含换行符,所以我们可以对其进行修剪(trim)。 println!("Hello, {}. Nice to meet you!", name.trim());}理解 .expect()
Section titled “理解 .expect()”.expect("message") 调用是一种快捷方式。如果 Result 是 Ok,它会返回内部的值。如果是 Err,它将导致程序 panic(崩溃)并显示你提供的消息。它对于示例和原型很有用,但在生产代码中,你通常应该使用 match 语句或其他方法进行更优雅的错误处理。
你已经看到了最简单的写入控制台的方法:println! 和 print! 宏。它们很方便,并为你处理格式化。
fn main() { let name = "Alice"; let age = 30; println!("Name: {}, Age: {}", name, age); // 打印后换行 print!("This will be on the "); // 打印后不换行 print!("same line.\n");}在底层,这些宏使用 std::io::stdout() 句柄。如果你需要更多控制,可以直接使用此句柄与 Write trait 配合,但 println! 通常就足够了。
处理命令行参数
Section titled “处理命令行参数”程序通常在命令行中带参数启动。Rust 的标准库提供了通过 std::env::args() 访问这些参数的简单方法。
std::env::args() 返回一个迭代器,它将参数作为字符串产生。第一个参数(args[0])始终是你的程序可执行文件的路径。
// 保存为 main.rs 并运行:`cargo run -- arg1 arg2`use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("All arguments: {:?}", args);
// 第一个参数是程序路径 let program_path = &args[0]; println!("Program path: {}", program_path);
// 访问其他参数 if args.len() > 1 { let first_arg = &args[1]; println!("First argument: {}", first_arg); }}一个更实际的例子:数字求和
Section titled “一个更实际的例子:数字求和”我们来创建一个程序,它对作为命令行参数传递的数字求和。这涉及到跳过第一个参数,并将其余参数从字符串解析为数字。这是一个迭代器大放异彩的绝佳示例。
// 运行:`cargo run -- 10 20 5`use std::env;
fn main() { let mut total = 0;
// env::args() 返回一个迭代器。 .skip(1) 创建一个新的迭代器, // 跳过第一个元素(程序路径)。 for arg in env::args().skip(1) { // .parse() 返回一个 `Result`,我们用 `match` 处理它。 match arg.parse::<i32>() { Ok(num) => { total += num; } Err(_) => { eprintln!("Error: '{}' is not a valid integer. Skipping.", arg); } } }
println!("Total sum: {}", total);}这个例子健壮得多。它使用 .skip(1) 进行地道的迭代,并使用 match 优雅地处理不是有效数字的参数,将错误打印到标准错误(eprintln!)而不是导致程序崩溃。
专业提示:使用 Crate 处理复杂参数
Section titled “专业提示:使用 Crate 处理复杂参数”对于任何实际应用,手动解析命令行参数可能会变得很繁琐。Rust 生态系统为此提供了优秀的库(称为 ‘crates’)。其中一个非常流行的是 clap。它允许你声明式地定义应用程序的 CLI 接口,并自动为你处理解析、验证和生成帮助消息。
虽然 std::env::args() 对于简单情况非常有用,但一旦你的需求超出少数位置参数,请考虑使用像 clap 这样的 crate。