配置开发环境
安装rust
因为我准备使用RustRover开发,所以选择在windows下配置rust环境。
在 Windows 上,前往官网 https://www.rust-lang.org/install.html 安装 Rust。
打开按流程安装就好
安装过程的某个步骤,会被提示要安装 Visual Studio,按要求安装就行。
安装好以后打开终端用rustc --verson
检查一下
如果安装完成但是终端识别不到rustc
指令,就重启一下终端或者手动配置系统环境变量再重启终端。
出现这样的画面就是安装好了
安装RustRover
去官网下载https://www.jetbrains.com/rust/,因为RustRover的个人非商用版免费,所以也不需要找破解版,正常使用就行。
安装及常见问题细节建议参照这篇博客
https://blog.csdn.net/tg928600774/article/details/133134741
教程
我使用的教程是这两个
Rust中文手册(更对新手友好一些)
https://kaisery.github.io/trpl-zh-cn/title-page.html
通过例子学Rust
https://rustwiki.org/zh-CN/rust-by-example/hello/print/fmt.html
rustc和cargo
rustc
Rust 源文件总是以 .rs
扩展名结尾。而rustc
是rust的官方编译器,效果类似gcc
吧。
main.rs
fn main() {
println!("Hello, world!");
}
> rustc main.rs
> .\main
Hello, world!
cargo
cargo
是rust的系统构建和包管理器
详见https://kaisery.github.io/trpl-zh-cn/ch01-03-hello-cargo.html
格式化
格式化输出
打印操作由 std::fmt
模块里面所定义的一系列宏来处理
std::fmt
包含多种 trait
(特质)来控制文字显示,其中重要的两种 trait 的基本形式如下:
fmt::Debug:使用 {:?}
标记。格式化文本以供调试使用。
fmt::Display:使用 {}
标记。以更优雅和友好的风格来格式化文本。
fmt::Display
fmt::Display 采用 {} 标记。
下面是常见用法示例:
fn main(){
// 通常情况下,`{}` 会被任意变量内容所替换。
// 变量内容会转化成字符串。
println!("{} days",31);
// 不加后缀的话,31 就自动成为 i32 类型。
// 你可以添加后缀来改变 31 的类型(例如使用 31i64 声明 31 为 i64 类型)。
// 用变量替换字符串有多种写法。
// 比如可以使用位置参数。
println!("{0},this is {1}. {1},this is {0}","Alice","Bob");
// 可以使用命名参数。
println!("{a},{b},{c}",a="A",b="B",c="C");
// 可以在 `:` 后面指定特殊的格式。
println!("{} of {:b} people know binary, the other half don't", 1, 2);
// 你可以按指定宽度来右对齐文本。
// 下面语句输出 "| 1|",5 个空格后面连着 1。
println!("|{number:>width$}|", number=1, width=6);
// 你可以在数字左边补 0。下面语句输出 "000001"。
println!("{number:>0width$}", number=1, width=6);
}
填充输出语句
// 你可以在数字左边补 0。下面语句输出 "000001"。
println!("{number:>0width$}", number=1, width=6);
在 Rust 的格式化语法中,0 是一个特殊的填充标记,用于指定当输出内容长度不足指定宽度时的填充字符。如果尝试将 0 换成其他数字(比如 1、2 等),会导致编译错误,这是由 Rust 格式化语法的规则决定的。
如果想使用 0 以外的字符(比如字母、符号或其他数字)作为填充符,需要在宽度前直接指定该字符,且该字符必须是非数字(因为数字会被解析为宽度的一部分)。
例如用*填充
println!("{number:>*width$}", number=1, width=6); // 输出:*****1
控制浮点数位数
let pi = 3.1415926;
println!("Pi is roughly {0:.1$}",pi,3)
注意用{:}
指定特殊格式时,如果用到格式化语句中的参数,后面要加$
fmt::Debug
所有的类型(就是数据结构),若想用 std::fmt
的格式化打印,都要求实现至少一个可打印的 traits
。仅有一些类型提供了自动实现,比如 std
库中的类型。所有其他类型都必须手动实现。
fmt::Debug
这个 trait
使这项工作变得相当简单。所有类型都能推导(derive
,即自动创建)fmt::Debug
的实现。(但是 fmt::Display
需要手动实现)
说人话就是,想要对数据结构进行格式化打印,大多数情况下都必须手动为具体类型编写 trait
所要求的方法代码,使该类型具备 trait
定义的能力。但是fmt::Debug
这个trait
,就可以通过#[derive(Debug)]
注解,让编译器自动生成 Debug 的实现,进行输出。
模块,trait,结构体和实现(impl)的关系
结构体
是“具体实体”,用来封装数据。比如现实中的 “狗”“猫” 是具体的动物,在代码中可以用结构体表示它们的属性(名字、年龄等)。
例:
// 结构体:表示"狗"这个实体,包含它的属性
struct Dog {
name: String,
age: u32,
}
// 结构体:表示"猫"这个实体
struct Cat {
name: String,
age: u32,
}
Trait
可以理解为 “行为规范 / 技能”,定义能做什么(方法声明),但不规定具体怎么做。比如 “会叫” 是一种行为,狗和猫都有这个行为,但具体叫的方式不同(汪汪 vs 喵喵)。
例:
// Trait:定义"会叫"这个行为规范(只声明方法,不实现)
trait Speak {
fn speak(&self); // 所有实现这个trait的实体,都必须有"叫"的方法
}
实现(impl)
给结构体 “绑定具体行为” 的过程。也就是为结构体实现 trait 中定义的方法(告诉编译器 “这个实体具体怎么完成这个行为”),或者给结构体添加它自己独有的方法。
例:
// 为Dog实现Speak trait:具体定义"狗怎么叫"
impl Speak for Dog {
fn speak(&self) {
println!(“{}(狗)汪汪叫!”, self.name); // 具体实现
}
}
// 为Cat实现Speak trait:具体定义"猫怎么叫"
impl Speak for Cat {
fn speak(&self) {
println!(“{}(猫)喵喵叫!”, self.name); // 具体实现
}
}
// 还可以给结构体添加独有的方法(不依赖trait)
impl Dog {
fn wag_tail(&self) { // 狗独有的"摇尾巴"方法
println!(“{}摇了摇尾巴~”, self.name);
}
}
模块(Module)
可以理解为 “分类文件夹”,用来组织相关的代码(结构体、trait、实现等),避免命名混乱。比如把所有动物相关的代码放在一个animal模块里,方便管理。
#![allow(unused)]
fn main() {
// 这个结构体不能使用 `fmt::Display` 或 `fmt::Debug` 来进行打印。
struct UnPrintable(i32);
// `derive` 属性会自动创建所需的实现,使这个 `struct` 能使用 `fmt::Debug` 打印。
#[derive(Debug)]
struct DebugPrintable(i32);
}
尽管使用fmt::Debug可以方便快捷地查看程序输出,但会牺牲掉很大一部分显示效果,Rust也为fmt::Debug提供了{:#?}
改善部分显示效果,如果希望控制程序输出的显示效果,还是需要手动实现,并使用fmt::Display
。
示例代码:
#[derive(Debug)]
struct Person<'a>{
name:&'a str,
age:u8
}
fn main(){
let name = "Peter";
let age = 27;
let peter = Person{name ,age };
println!("{:?}",peter);
}
输出:
Person { name: "Peter", age: 27 }
改用{:#?}
println!("{:#?}",peter);
输出:
Person {
name: "Peter",
age: 27,
}
格式化和安全
我们已经看到,格式化的方式是通过格式字符串来指定的。
格式化的功能是通过 trait
实现的,每种参数类型都对应一种 trait
。最常见的格式化 trait 就是 Display
,它可以处理参数类型为未指定的情况,比如 {}
。
在Rust中,格式化字符串和格式化字符串的参数一一对应,避免了非法读写的格式化字符串漏洞风险。