Rust基础入门二

流程控制

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中多态实现机制

  1. 静态分发(单态化):编译期确定类型,性能高,无运行开销
  2. 动态分发(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> 内部存两个指针:

  1. 数据指针:指向Dog/Cat实例
  2. 虚表(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必须对象安全

  1. 所有方法不能有泛型
  2. 方法接收self只能是:&self/&mut self,不能是self(所有权转移)
  3. 方法返回类型不能是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());
}
相关推荐
doiito4 小时前
【安全,架构】RustyVault 项目深度分析报告以及和HashiCorp Vault的差异
后端·安全·rust
Rockbean4 小时前
10分钟Solana-性能web3-5.4 项目搭建核心环节
rust·web3·智能合约
Rockbean4 小时前
10分钟Solana-性能web3-5.3 项目搭建
rust·web3·智能合约
程序员爱钓鱼5 小时前
Rust 项目目录结构详解:从 Hello World 到企业级项目
后端·rust
2601_9615934215 小时前
Rust 开发环境配置繁琐?RustRover 开箱即用搞定编码调试
开发语言·后端·macos·rust
冬奇Lab17 小时前
每日一个开源项目(第154篇):Warp - 从‘好看的终端‘到 Agentic 开发环境
人工智能·rust·llm
想你依然心痛1 天前
内存安全语言在嵌入式中的对比:Rust vs Ada vs SPARK——形式化验证、运行时
安全·rust·spark
喜欢打篮球的普通人1 天前
Trition程序编写:从“Hello CUDA“到“Hello Triton“:向量加法背后的编译黑魔法
开发语言·后端·rust
独孤留白1 天前
从C到Rust:Trait Copy 数据位拷贝属性
rust