程序跑起来后,可以先用 println! 看变量的值。把下面的完整代码放进入门页创建的 src/main.rs,执行 cargo run:
fn main() { let temperature = 10; println!("Hello, world!"); println!("今天是{},天气{}", "周二", "晴朗"); println!("温度 {temperature} 度");}let 声明变量,{temperature} 把这个变量的值放进文本。println! 是 macro 调用,末尾的 ! 是其语法的一部分;不要写成普通函数调用 println(...)。
文本输出到哪里
| macro | 结果或去向 | 自动添加换行 |
|---|---|---|
format! | 返回 String | 否 |
print! | stdout | 否 |
println! | stdout | 是 |
eprint! | stderr | 否 |
eprintln! | stderr | 是 |
format! 只生成字符串,不会自动打印。stdout 和 stderr 是不同的输出流,终端可能同时显示它们,重定向时可以分别处理。
占位符怎样选参数
fn main() { let user = "me"; assert_eq!(format!("{user} play {game}", game = "my game"), "me play my game"); assert_eq!(format!("{1} {} {0} {}", 1, 2), "2 1 1 2"); assert_eq!(format!("{} {0}", 10), "10 10");}assert_eq! 比较左右两边是否相等,不相等会 panic。这个程序正常结束时没有输出,说明这些比较通过了。
以第二行格式化为例:
| 占位符 | 取哪个参数 | 值 |
|---|---|---|
{1} | 显式指定索引 1 | 2 |
第一个 {} | 隐式索引 0 | 1 |
{0} | 显式指定索引 0 | 1 |
第二个 {} | 隐式索引 1 | 2 |
显式索引不会推进 {} 的隐式计数。这里讨论的是普通占位符;动态精度 {:.*} 还会消耗一个参数,详见文末的 std::fmt 文档。
不能多传格式字符串没有用到的参数。下面预期编译失败,错误中会出现 argument never used:
fn main() { println!("{} {0}", 10, 20);}两个占位符都用了 10,第二个参数 20 没被使用。应删除 20,或添加使用它的占位符。
宽度、填充、进制与精度
冒号前指定参数,冒号后指定显示方式。例如 {number:0>width$} 中,number 是值,0 是填充字符,> 表示右对齐,width$ 从名为 width 的参数读取最小宽度。
fn main() { assert_eq!(format!("{:b} {:o} {:x}", 8, 8, 8), "1000 10 8"); assert_eq!(format!("{number:>5}", number = 1), " 1"); assert_eq!(format!("{number:0>width$}", number = 1, width = 8), "00000001"); assert_eq!(format!("{:.3}", std::f64::consts::PI), "3.142"); assert_eq!(format!("{{value}} = {}", 8), "{value} = 8");}宽度是最小宽度,数值更长时不会被截断。对浮点数,.3 指小数点后显示三位,会舍入;把 3.14 格式化成三位只会得到 3.140,不会补回原来就没有的精度。{{ 和 }} 用来输出字面上的花括号。
Display 与 Debug
{} 使用 Display trait;{:?} 使用 Debug trait。trait 在这里可以先理解为类型提供的一组约定行为。类型支持哪种格式,取决于它实现了哪些 trait。
#[derive(Debug)]struct Point { x: i32, y: i32,}
fn main() { let point = Point { x: 3, y: 4 }; println!("坐标:({}, {})", point.x, point.y); println!("{point:?}"); println!("{point:#?}");}#[derive(Debug)] 为这个 struct 生成 Debug 实现,{:#?} 请求更易读的展开格式。它不会同时生成 Display,因此直接写 println!("{point}") 仍会报错。Debug 适合检查值,不宜依赖其具体排版作为稳定的数据交换格式。
参考:std::fmt。下一篇从 let 和类型推断开始,说明这些变量能存什么。
评论