前端学Rust

99 阅读9分钟

先自上而下学

Rust的包管理工具Cargo

创建

cargo new project_name

创建一个新的 rust 库项目

cargo new --lib project_name

构建

cargo build

生成可执行文件

cargo build --release

检测

cargo check

运行/测试

cargo run/cargo test

项目结构

  • project_name/

    • Cargo.toml
    • src
      • lib.rs
  • 可执行的二进制

  • project_name/

    • Cargo.toml
    • src
      • main.rs

Cargo.toml

  • package

    • 设置项目名称
    • 版本
  • dependencies

    • 设置依赖
    • [build-dependencies]在构建项目时需要的依赖项
    • [dev-dependencies]开发时需要的依赖项

Rust第三方库

crates.io

image.png 执行添加命令或者复制进 toml 配置文件

通过命令管理依赖

  • 添加库
    • cargo add dependency_name
  • 安装指定版本
    • cargo add dependency_name@version
  • 添加开发时用的依赖
    • cargo add --dev dependency_name
  • 添加构建时用的依赖
    • cargo add --build denpendency_name
  • 删除库
    • cargo rm dependency_name

配置镜像源

rsproxy.cn/

image.png

基础

变量和不可变性

  1. let 关键字声明变量
let x: i32 = 5
  1. 支持类型推导也可以显式指定变量类型

  2. 变量名使用蛇形,枚举和结构体使用帕斯卡,如果变量名没有用到可以前面加下划线来消除警告

  3. 强制类型转换

let a = 9.1
let b = a as i32
  1. 打印变量
println!("val: {x}")
  1. rust 的变量是不可变的,如果希望变量可变需要如下声明
let mut y =10
y =20
  1. rust 允许隐藏一个变量,这意味着可以声明一个与现有变量同名的新变量,从而有效地隐藏前一个变量

Const 常量

  1. 常量的值必须在编译时已知的常量表达式,必须指定类型与值
  2. Rust的 const 常量的值被直接嵌入到生成的底层机器代码
  3. 常量名与静态变量命名必须全部大写,单词之间加入下划线
  4. 常量的作用域是块级,只在作用域内可见
    const SECOND_HOUR: usize = 60;
    const SECOND_DAY: usize = SECOND_HOUR * 24;
    println!("{}", SECOND_DAY);

static静态变量

  1. static 变量在运行时分配内存的
  2. 并不是不可变的,可以使用unsafe 修改
  3. 静态变量的生命周期为整个程序的运行时间
static  MY_STATIC_VAR: i32 = 1;
fn main() {
    let a = 1;
    println!("var: {a}");
    println!("Hello, world!");

    const SECOND_HOUR: usize = 60;
    const SECOND_DAY: usize = SECOND_HOUR * 24;
    println!("{}", SECOND_DAY);
    println!("{}", MY_STATIC_VAR);
}

基础数据类型

  • integer types 默认推断为:i32
    • i8、i16、i32、i64、i128
  • unsigned integer types
    • u8、u16、u32、u64、u128
  • platform-specific integer type(由平台决定)
    • unsize
    • isize
  • float types
    • f32
    • f64(尽量使用)
  • boolean values
    • true
    • false
  • character types
    • rust支持 unicode
    • 表示 char 类型使用单引号

元组与数组

  • 相同点
    • 元组和数组都是 compound Types
    • 元组和数组长度都是固定的
  • Tuples 不同类型的数据类型
  • Arrays 同一类型的数据类型

数组

数组是固定长度的同构集合

  • 创建方式
    • [a,b,c]
    • [value;size]
  • 获取元素 arr[index]
  • 获取长度 arr.len()

以下是一个值传递的例子

fn print_fn(mut arr: [&str; 3]) {
    for item in arr {
        if item == "Tom" {
            arr[0] = "";
        }
    }
    println!("{:?}", arr);
   
    let mut arr2: [&str; 3] = ["Tom", "Jerry", "Mickey"];
    print_fn(arr2);
    println!("{:?}", arr2)

元组

元组是固定长度的异构集合

  • empty tuple():函数的默认返回值
  • 元组获取元素
    • tup.index
    • 没有 len()
    let tup = (1, "123", true);
    println!("element: {}, {}, {}", tup.0, tup.1, tup.2);

    let arr = [1, 2, 3];
    println!("first element: {}", arr[0]);

循环

for

// 1..5 左闭右开
    for num in 1..5{
        println!("{}", num);
    }
// 1..5 左闭右闭
        for num in 1..=5{
        println!("{}", num);
    }

遍历

    let books = vec![
        "The C Programming Language",
        "The UNIX Programming Environment",
        "The Linux Command Line"
    ];
   for book in books{
       println!("{}", book);
   } 

iter借用集合中的一个元素,元素本身不会改变,循环之后还可以使用

    for book in books.iter(){
        match book {
            &
            "The C Programming Language" => println!("C is awesome!"),
            _ => println!("Just an ordinary book.")
            
        }
    }

into_iter会消耗集合,每次迭代,集合中的数据本身被提供,一旦消耗完毕,则后续无法使用,因为元素被 move 了

    for book in books.into_iter() {
        match book {
            "The C Programming Language" => println!("C is awesome!"),
            _ => println!("Just an ordinary book."),
        }
    }

iter_mut用于修改元素

    let mut books1 = vec![
        "The C Programming Language",
        "The UNIX Programming Environment",
        "The Linux Command Line",
    ];
    for book in books1.iter_mut() {
        *book = match book {
            &mut "The C Programming Language" => "C Programming Language, 2nd Edition",
            _ => book,
        };
        println!("{}", book)
    }

while

    let mut count = 2;
    while count <= 20 {
        println!("count is {}", count);
        count *= 2;
    }

loop

    let mut num = 3;
    loop {
        if num >= 20 {
            break;
        }
        println!("num is {}", num);
        num *= 2
    }

函数

fn 函数名称([参数:参数类型]) -> 返回值{} 无明确返回值的时候,返回一个单元类型()

不写 return 的时候,默认后一句作为返回值

    fn get_name() -> String{
        return String::from("Hello");
    }
    fn get_name1() -> String{
        String::from("Hello")
    }

值传递和引用传递

// 值传递,在函数内部创建了一个新的变量
fn double_price(mut price: f64){
    price *= 2.0;
    println!("The price is {}", price);
}

// 引用传递,传递的变量和函数参数指向同一个内存地址,*是用于访问引用地址,&进行引用传递
fn double_price1(price: &mut f64){
    *price *= 2.0;
    println!("The price is {}", price);
}

fn main() {
    let mut price = 10.0;
    double_price(price);
    println!("The price is {}", price);

    let mut price1 = 10.0;
    double_price1(&mut price1);
    println!("The price is {}", price1)
}

所有者问题

最后一行的 name 已经不存在了,所有权交给了函数

    fn show_name(name: String){
        println!("The name is {}", name);
    }
    ...
    let name = String::from("Tom");
    show_name(name);
    println!("name is {name}") // 失效

ownership 与结构体、枚举

rust的内存管理模型

  • 所有权系统(ownership system)
  • 借用(borrowing)
    • 不可变引用(不可变借用)
    • 可变引用(可变借用)
  • 生命周期(lifetimes)
  • 引用计数(reference counting)
    let s1 = String::from("value");
    let s2 = s1;
    println!("s1: {s1}") //无法运行,所有权在 s2
fn get_string(s: String){
    println!("{}", s);
}
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    get_string(s1);
}

相当于所有权丢到了函数里面,函数执行完毕之后 s1会被销毁,例如想要在 get_string 执行后打印s1 是无法打印的,因为在函数执行完后被销毁了

所有者

  • 栈 后进先出,类型太小是固定的
  • 堆 编译时大小未知或不确定大小,用户自己管理

获得所有权的方式,

fn show(vec: Vec<&str>) -> Vec<&str>{
    println!("{:?}", vec);
    return vec;
}

let vec = vec!["1", "2"];
let vec = show(vec);
println!("{:?}", vec);

借用所有权

借用就是一个函数中的变量传递给另一个函数作为入参使用,也要求函数参数离开自己作用域的时候将所有权还给当初传递给它的变量

&变量名

发生借用后,变量就回来了

fn show(vec: &Vec<&str>){
    println!("{:?}",vec);
}
fn main() {
    let nameList = vec!["Alice","Bob","Cathy","David","Eve"];
    show(&nameList);
    let nameList2 = nameList;
    println!("{:?}",nameList2);
}

两次打印的结果第一个元素都是空字符,因为都是一个内存地址的引用

let mut nameList3 = vec!["Alice","Bob","Cathy","David","Eve"];
changeVec(&mut nameList3);
println!("{:?}",nameList3);
fn changeVec(vec: &mut Vec<&str>){
    vec[0] = "";
    println!("{:?}",vec);
}

切片

let 切片值 = &变量[起始位置..结束位置]

普通切片和可变切片

fn show_slice(slice: &[&str]){
    println!("{:?}", slice);
}

fn modify_slice(slice: &mut [&str]){
    slice[0] = "hello";
    println!("{:?}", slice);
}
fn main() {
    let v1 = vec!["qqq", "world"];
    show_slice(&v1[..]);
    let mut v2 = vec!["qqq", "world"];
    modify_slice(&mut v2[..]);
    println!("{:?}", v2)
}

string与&str

  • string 是一个堆分配的可变字符串类型
  • &str 是指字符串切片引用,是在栈上面分配的
    • 不可变引用,指向存储在其他地方的 utf-8 编码的字符串数据
    • 有指针和长度构成

如何选择

string 是具有所有权的,而&str(字面量)并没有

  • struct 中属性使用 string

    • 如果不使用显式声明生命周期无法使用&str
    • 不只是麻烦,还有更多的隐患
  • 函数参数推荐使用&str(如果不想交出所有权)

    • &str 为参数,可以传递&str 和&string
    • &string 为参数,只能传递&String 不能传递&str
  • String::form

  • to_string()

  • to_owned()

fn get_string(s: String){
    println!("{}", s);
}
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    get_string(s1);
    
    let name = String::from("value c++");

     let course = "Rust".to_string();
     let new_name = name.replace("c++", "CPP");
     println!("{name} {course} {new_name}");

     let rust = "Rust";
     println!("{rust}");

}

在结构体中使用&str,标注生命周期


struct Person<'a>{
    name: &'a str,
    age: i32,
}

let person = Person{
 name: username,
 age,
};

Enum枚举

枚举是一种用户自定义的数据类型,用于标识一组离散可能值的变量

  • 每种可能值都成为“variant”(变体)
  • 枚举名::变体名

常用的枚举类型:option 和 result

enum Option<T>{
    Some(T), // 用于返回一个值
    None //用于返回 null,用 none 来代替
}

匹配模式

  1. match 的关键字实现
  2. 必须覆盖所有的变体
  3. 可以用_、..=、三元if 来进行匹配
enum BuildingLocation {
   Number(i32),
   Street(String),
   Unknown
}

impl BuildingLocation {
    fn print_location(&self) {
        match self {
            BuildingLocation::Number(c) => println!("The building is at number {}", c),
            BuildingLocation::Street(s) => println!("The building is at street {}", s),
            BuildingLocation::Unknown => println!("The location is unknown"),
        }
    }
    
}

// main
let building_location = BuildingLocation::Street("main street".to_string());
    building_location.print_location();

结构体、方法、关联函数、关联变量

结构体

结构体是一种用户定义的数据类型,用于创建自定义的数据结构

struct Point {
    x: i32,
    y: i32
}

每条数据(x 和 y)成为属性(字段 field),通过.来访问结构体中的属性

结构体中的方法

这里的方法是指,通过实例调用(&self、&mut self、self)

结构体中的关联函数

关联函数是与类型相关联的函数,调用时为结构体名::函数名

结构体中关联变量

和结构体相关联的变量,也可以在特质或是枚举中,通过 xxx::yyy 调用

enum Flavor {
    Spicy,
    Sweet,
    Fruity
}

struct Drink {
    flavor: Flavor,
    price: f64
}

impl Drink{
    fn buy(&self){
        if self.price > 10.0 {
           println!("Too expensive!") 
        }
    }
}

fn print_drink(drink: Drink) {
    match drink.flavor {
       Flavor::Fruity => println!("Fruit drink"),
       Flavor::Spicy => println!("Spicy drink"),
       Flavor::Sweet => println!("Sweet drink")
    }
    println!("Price: {}", drink.price)
}

fn main() {
    let drink = Drink {
        flavor: Flavor::Spicy,
        price: 1.99
    };
    drink.buy();
    print_drink(drink);
}

集合

向量 vector

  • 相同类型的元素集合
  • 长度可变,运行时增加减少都可以
  • 使用索引查找
  • 添加元素到队尾
  • 向量存在堆上,长度可以动态变化
Vec::new()
vec![...]

方法

  • remove:移除元素
  • contains:查找元素

HashMap

键值对的集合,显示导入std""collection

    let mut map = HashMap::new();
    map.insert("tom", 10);
    map.insert("jerry", 20);

    match map.get("tom"){
        Some(value) => println!("{}", value),
        None => println!("not found"),
    }
    println!("{:?}", map)
    
    for (k, v) in map.iter(){
        println!("{}: {}", k, v);
    }

HashSet

相同数据类型的集合,且没有重复的值

    let mut hash_set = HashSet::new();
    hash_set.insert("tom");
    hash_set.insert("jerry");
    hash_set.insert("tom");
    println!("{:?}", hash_set);

泛型

rust 中的泛型主要包含泛型集合、泛型结构体、泛型函数、泛型枚举和特质

let mut v:Vec<i32> = vec![1,2,3]

特质

struct Book {
    name: String,
    author: String,
    number: i32,
}

trait BookTrait {
    fn showBook(&self);
}

impl BookTrait for Book {
    fn showBook(&self) {
        println!("{} {} {}", self.name, self.author, self.number);
    }
}

fn main() {
    let book = Book {
        name: String::from("Rust"),
        author: String::from("Rust"),
        number: 100,
    };
   book.showBook();
}