流程控制
if-else if-else
rust
fn main() {
let n = 6;
if n % 4 == 0 {
println!("number is divisible by 4");
} else if n % 3 == 0 {
println!("number is divisible by 3");
} else if n % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
循环控制
for in
rust
for i in 1..=5 {
println!("{}", i);
}
| 使用方法 | 等价使用方式 | 所有权 |
|---|---|---|
for item in collection |
for item in IntoIterator::into_iter(collection) |
转移所有权 |
for item in &collection |
for item in collection.iter() |
不可变借用 |
for item in &mut collection |
for item in collection.iter_mut() |
可变借用 |
while
rust
fn main() {
let mut n = 0;
while n <= 5 {
println!("{}!", n);
n = n + 1;
}
println!("我出来了!");
}
loop
rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
- break可以单独使用,也可以带一个返回值
- loop是一个表达式
模式匹配
出没于函数式编程里
match 匹配
rust
enum Direction {
East,
South,
West,
North,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East => { println!("east"); }
Direction::South | Direction::West => {
println!("south or west");
}
_ =>{
println!("north");
}
}
}
match 表达式赋值
rust
let addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let ip_str = match addr {
IpAddr::V4(ip) => {
"127.0.0.1"
}
IpAddr::V6(ip) => {
"other"
}
};
macth 模式绑定
rust
enum Action {
Say(String),
MoveTo(i32, i32),
ChangeColorRGB(u8, u8, u8),
}
fn main() {
let actions = [
Say("Hello, world!".to_string()),
Action::MoveTo(100, 200),
Action::ChangeColorRGB(255, 0, 255),
];
for action in &actions {
match action {
Say(s) => {
println!("{}", s);
}
Action::MoveTo(x, y) => {
println!("move to {}, {}", x, y);
}
Action::ChangeColorRGB(r, b, _) => {
println!("change color RGB to {}, {}", r, b);
}
}
}
}
if let匹配
有时候只需要一个模式值需要被处理,忽略其他情况
rust
let v = Some(5);
if let Some(5) = v {
println!("five")
}
matches! 宏
rust
#[derive(Debug)]
enum MyEnum{
Foo,
Bar
}
fn main() {
let v = vec![MyEnum::Foo, MyEnum::Bar];
let filter = v.iter().filter(|x| matches!(x, MyEnum::Bar));
filter.for_each(|x| println!("{:?}", x));
let foo = 'f';
let contains_foo = matches!(foo,'a'..='z'| 'A'..='Z');
println!("{}", contains_foo);
let bar = Some(4);
println!("{:?}", matches!(bar, Some(x) if x > 2));
}
模式适用场景
模式是Rust中的特殊语法,用来匹配类型中的结构和数据,它往往和match表达式联用,以实现强大的模式匹配能力。模式一般由以下内容组成:
- 字面值
- 解构的数组、枚举、结构体或者元组
- 变量
- 通配符
- 占位符
if let 分支
匹配一个,忽略剩下
rust
if let PATTERN = SOME_VALUE {
}
while let
只要模式匹配就一直循环
rust
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
while let Some(i) = v.pop() {
println!("{}", i);
}
}
for循环
rust
fn main() {
let v = vec!['a', 'b', 'c', 'd', 'e', 'f'];
for (index, value) in v.iter().enumerate() {
println!("{} - {}", index, value);
}
}
let语句
ini
let x = 5;
let (x, y, z) = (1, 2, 3);
方法Method
定义方法
rust
struct Circle{
x:f64,
y:f64,
radius:f64,
}
impl Circle{
fn new(x:f64,y:f64,radius:f64)->Circle{
Circle{x,y,radius}
}
fn area(&self)->f64{
std::f64::consts::PI*(self.radius*self.radius)
}
}
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
self、&self和&mut self区别
- self表示Rectangle的所有权转移到该方法中,这种形式用的较少
- &self表示该方法对Rectangle的不可变借用
- &mut self表示可变借用
泛型和特征
泛型Generics
结构体中使用泛型
rust
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let point = Point { x: 1, y: 'z' };
}
枚举泛型
rust
enum Option<T> {
Some(T),
None,
}
方法泛型
rust
struct Point<T>{
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
impl Point<f32> {
fn y(&self) -> f32 {
self.y
}
}
const 泛型
针对值的泛型 回顾数组,数组长度不同,类型不同
rust
fn display_array(arr:[i32;3]){
println!("{:?}", arr);
}
fn main() {
let arr:[i32;3] = [1, 2, 3];
display_array(arr);
let arr:[i32;2] = [1, 2];
display_array(arr)
}
编译报错 此时需传递数组切片
rust
fn display_array(arr:&[i32]){
println!("{:?}", arr);
}
fn main() {
let arr:[i32;3] = [1, 2, 3];
display_array(&arr);
let arr:[i32;2] = [1, 2];
display_array(&arr)
}
接着将i32改成接收所哟类型的数组
rust
use std::fmt::{Debug};
pub mod rust_base;
fn display_array<T>(arr: &[T])
where
T: Debug,
{
println!("{:?}", arr);
}
fn main() {
let arr: [i32; 3] = [1, 2, 3];
display_array(&arr);
let arr: [i32; 2] = [1, 2];
display_array(&arr)
}
用const 泛型解决
rust
fn display_array<T:Debug,const N:usize>(arr: [T;N])
where
T: Debug,
{
println!("{:?}", arr);
}
fn main() {
let arr: [i32; 3] = [1, 2, 3];
display_array(arr);
let arr: [i32; 2] = [1, 2];
display_array(arr)
}
const fn 常量函数
为什么需要 某些场景下,我们希望在编译期就计算出一些值,以提高运行性能 基本用法
rust
const fn add(a:usize,b:usize) -> usize {
a + b
}
const RESULT:usize = add(5,10);
fn main() {
println!("{}", RESULT);
}
const fn限制 无论在编译器还是运行时调用 const fn,他们的结果总是相同,即使多次调用也是如此。唯一的例外是,如果您在极端情况下进行复杂的浮点操作,可能会得到不同结果。因此,不建议使用 数组长度(arr.len())和Enum判别式依赖于浮点计算 结合const fn与const泛型
rust
struct Buffer<const N:usize>{
data: [u8; N],
}
const fn compute_buffer_size(factor:usize)->usize{
factor*1024
}
fn main() {
const SIZE:usize = compute_buffer_size(4);
let buffer = Buffer::<SIZE> {
data: [0; SIZE],
};
println!("{}", buffer.data.len());
}
特征 Trait
特征定义
rust
pub trait Summary{
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
pub struct Post{
title: String,
content: String,
author: String,
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
pub struct Weibo{
username: String,
content: String,
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
fn main() {
let post = Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()};
let weibo = Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()};
println!("{}",post.summarize());
println!("{}",weibo.summarize());
}
使用特征作为函数参数
rust
pub fn notify(item:&impl Summary){
println!("{}",item.summarize());
}
特征约束
impl Trait语法糖
rust
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
多重约束
rust
pub fn notify(item: &(impl Summary + Display)) {}
等价于
rust
pub fn notify<T: Summary + Display>(item: &T) {}
where约束
rust
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {}
太复杂了,简便方式
rust
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
U: Clone + Debug
{}
先定义类型,再通过where 进行约束
函数返回中 impl Trait
rust
fn returns_summarizable() -> impl Summary {
Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()}
}
这种返回有一个很大限制,只能返回一个具体类型
rust
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
Post {
title: String::from(
"Penguins win the Stanley Cup Championship!",
),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
}
} else {
Weibo {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
}
}
}
以上的代码就无法通过编译,因为它返回了两个不同的类型 Post 和 Weibo
shell
`if` and `else` have incompatible types
expected struct `Post`, found struct `Weibo`
报错提示我们 if 和 else 返回了不同的类型。如果想要实现返回不同的类型,需要使用特征对象
特征对象
rust
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
Post {
// ...
}
} else {
Weibo {
// ...
}
}
}
代码无法通过编译问题
Rust中多态实现机制
- 静态分发(单态化):编译期确定类型,性能高,无运行开销
- 动态分发(Trait Object
Box<dyn Trait>/&dyn Trait):运行时确定类型,支持集合存储不同多态类,是真正的多态
- Triat:定义统一行为接口
- dyn Trait:特征对象,实现动态多态,要求trait满足 对象安全 (仅包含方法无泛型、self 为
&self/&mut self)
静态分发(编译期多态,泛型)
rust
trait Animal{
fn speak(&self);
}
struct Dog;
impl Animal for Dog{
fn speak(&self) {
println!("汪汪汪");
}
}
struct Cat;
impl Animal for Cat{
fn speak(&self) {
println!("喵喵喵")
}
}
fn make_sound<T:Animal>(animal:T){
animal.speak();
}
fn main() {
let dog = Dog;
let cat = Cat;
make_sound(dog);
make_sound(cat);
}
泛型函数会为每一个传入类型单独生成代码,编译期绑定,速度最快
特点
- 编译期单态化,无虚表、无运行时开销
- 缺点:无法把 Dog、Cat 放进同一个 Vec(类型不同)
动态分发(Trait Object,真正多态,推荐业务使用)
使用 Box<dyn Animal> 特征对象,不同实现类可存入同一容器,运行时自动匹配实现,是 Rust 标准多态写法
rust
// 统一行为接口
trait Animal {
fn speak(&self);
}
// 实现1:狗
struct Dog {
name: String,
}
impl Animal for Dog {
fn speak(&self) {
println!("{}:汪汪汪", self.name);
}
}
// 实现2:猫
struct Cat {
name: String,
}
impl Animal for Cat {
fn speak(&self) {
println!("{}:喵喵喵", self.name);
}
}
// 接收任意实现 Animal 的特征对象
fn make_sound(animal: &dyn Animal) {
animal.speak();
}
fn main() {
// 把不同类型存入同一个 Vec<Box<dyn Animal>>
let mut animals: Vec<Box<dyn Animal>> = Vec::new();
animals.push(Box::new(Dog { name: "旺财".into() }));
animals.push(Box::new(Cat { name: "咪咪".into() }));
// 遍历统一调用,多态生效
for animal in animals {
animal.speak();
}
// 单独调用
let dog = Dog { name: "小黑".into() };
make_sound(&dog);
}
动态分发原理 Box<dyn Animal> 内部存两个指针:
- 数据指针:指向Dog/Cat实例
- 虚表(vtable)指针:指向该类型实现Animal的方法列表,运行时查表调用函数
带可变方法的多态(&mut dyn Trait)
如果trait需要修改自身,使用 &mut dyn
rust
// 统一行为接口
trait Animal {
fn speak(&self);
fn rename(&mut self, new_name: &str);
}
struct Dog {
name: String,
}
impl Animal for Dog {
fn speak(&self) {
println!("{}:汪汪", self.name);
}
fn rename(&mut self, new_name: &str) {
self.name = new_name.to_string();
}
}
fn main() {
let mut dog = Dog { name: "Dog".to_string() };
let mut animal:&mut dyn Animal = &mut dog;
animal.speak();
animal.rename("大黄");
animal.speak();
}
关键限制:Trait对象安全规则 想要dyn Trait合法,trait必须对象安全
- 所有方法不能有泛型
- 方法接收self只能是:&self/&mut self,不能是self(所有权转移)
- 方法返回类型不能是Self(这是大写Self,指代当前特征或方法类型别名)
错误示例
rust
trait BadTrait {
// 带泛型,不安全
fn foo<T>(&self, x: T);
// 获取所有权self,不安全
fn bar(self);
}
静态分发与动态分发对比
表格
| 方式 | 实现 | 优点 | 缺点 | 使用场景 |
|---|---|---|---|---|
| 静态分发 | 泛型 T: Trait |
零运行开销,性能最高 | 不能存储不同类型到同一集合 | 性能敏感、类型固定场景 |
| 动态分发 | Box<dyn Trait> / &dyn Trait |
支持多态容器,统一管理多种实现 | 运行时虚表查找,轻微性能损耗 | 插件、多类型统一处理(GUI、硬件驱动) |
Self 与 self
在Rust中,有两个self,一个指代当前实例对象,一个指代特征或者方法类型别名
rust
trait Draw{
fn draw(&self)->Self;
}
#[derive(Clone)]
struct Button;
impl Draw for Button {
fn draw(&self)->Self{
self.clone()
}
}
fn main() {
let button = Button;
let new_b = button.draw();
}
进一步深入特征
关联类型
关联类型是在特征定义的语句块中,生明一个自定义类型,这样就可以在特征的方法签名中使用该类型:
rust
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
}
}
fn main() {
let c = Counter{..}
c.next()
}
Self 用来指代当前调用者的具体类型,那么 Self::Item 就用来指代该类型实现中定义的 Item 类型
默认泛型类型参数
rust
use std::ops::Add;
#[derive(Debug,PartialEq)]
struct Point{
x: i32,
y: i32,
}
impl Add for Point{
type Output = Point;
fn add(self, rhs: Self) -> Self::Output {
Point{x: self.x + rhs.x, y: self.y + rhs.y}
}
}
fn main() {
let point = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
println!("{:?}", point);
}
调用同名的方法
rust
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
fn main() {
let person = Human;
// person.fly();
Pilot::fly(&person); // 调用Pilot特征上的方法
Wizard::fly(&person); // 调用Wizard特征上的方法
person.fly(); // 调用Human类型自身的方法
}
- 默认调用自身方法
- 其次调用特征方法
rust
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
println!("A baby dog is called a {}", Dog::baby_name());
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}