Rust学习笔记

Rust学习笔记

1.0 Rust概述

最近因为接触到一个有趣的前端桌面应用新方案tauri,由于tauri的后端是rust,因此提起了我对rust的兴趣。

Rust语言的特点

  • 高性能 - Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
  • 可靠性 - Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
  • 生产力 - Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具 —— 包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。

rust适合的领域

  • 网络编程
  • 游戏编程
  • web后端与wasm
  • 命令行工具
  • 操作系统
  • 嵌入式

1.1 安装、配置开发环境

安装rust

这里不过多赘述。

linux
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

如果安装成功,将出现下面这行:

Rust is installed now. Great!
windows

rust编译工具:https://www.rust-lang.org/zh-CN/tools/install)

Windows 上安装 Rust 需要有 C++ 环境,有两种方式可供选择:

  1. x86_64-pc-windows-msvc(官方推荐)
  2. x86_64-pc-windows-gnu

在安装时可自行选择。

配置开发环境

推荐vscode作为rust的开发工具

Visual Studio Code下载地址:https://code.visualstudio.com/Download

插件:

  • rust-analyzer(必装)
  • Better TOML(推荐)
  • crates(推荐)
  • Error Lens(推荐)

1.2 cargo

Cargo 是什么

Cargo 是 Rust 的构建系统和包管理器。

Rust 开发者常用 Cargo 来管理 Rust 工程和获取工程所依赖的库。

cargo常用指令

  • cargo build 构建你的项目
  • cargo run 运行你的项目
  • cargo test 测试你的项目
  • cargo doc 构建你的项目的文档
  • cargo publish 发布你的库至crates.io

快速开始

cargo new [项目名]

修改国内源

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
# 指定镜像
replace-with = '镜像源名' # 如:tuna、sjtu、ustc,或者 rustcc 
# 注:以下源配置一个即可,无需全部 
# 中国科学技术大学
[source.ustc]
registry = "https://mirrors.ustc.edu.cn/crates.io-index"
# >>> 或者 <<<
registry = "git://mirrors.ustc.edu.cn/crates.io-index" 
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index/" 

# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" 

# rustcc社区
[source.rustcc]
registry = "https://code.aliyun.com/rustcc/crates.io-index.git"

添加包(依赖)

比如添加一个随机数的包

只需去crates.io搜索rand

image-20220707171603881

将其添加至Cargo.toml中的[dependencies]

image-20220707172103287

2.1 rust变量和基础数据类型

变量

变量是什么
  • 将数据分配到临时内存位置,为了让程序员更好地操作内存
  • 可以被赋值于任何的值和类型
  • rust中let声明变量
  • rust中的变量默认是不可变的!,前置 mut可以使其为可变的
    • Immutable
    • Mutable

输入一个字符串并输出,main.rs:

// 标准库io包
use std::io;
fn main() {
    let mut input: String = String::new();
    println!("Hello, world!");
    match io::stdin().read_line(&mut input) {
        Ok(_) => {
            println!("Wow : {}", input);
        }
        _ => {}
    }
}

命令行执行cargo run

rust 基本数据类型

Integer 整型
SizeSignedUnsigned
8 biti8u8
16 biti16u16
32 biti32u32
64 biti64u64
128 biti128u128
arch(系统架构有关)isizeusize
Float 浮点型
SizeFloat
32 bitf32
64 bitf64
Boolean 布尔型
true
false
Character 字符类型

单引号

let a = 'a';
let smile = '\u{1f601}';
&str 字符串类型

双引号

let cat: &str = "car";

tips:若不写类型也会自动推导,如下:

image-20220707164443315

!表示宏

println!:打印信息到终端

有关于宏,我会在后续高级教程中再去写一遍,宏是rust中比较复杂的一个东西,大致分为两类:声明式宏( declarative macros ) macro_rules! 和三种过程宏( procedural macros ):

  • #[derive],在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 Debug 特质
  • 类属性宏(Attribute-like macro),用于为目标添加自定义的属性
  • 类函数宏(Function-like macro),看上去就像是函数调用

2.2 控制流(control flow)

Execution Flow

语句

if
else if
else

循环

loopingiteration

关键字:

  • loop无尽的循环

  • while有条件的循环

  • for

  • break

  • continue

loop and while

因为loop是无尽的循环,所以需要通过break退出:

image-20220707170928433

for
fn main() {
    for 元素 in 集合 {
      // 使用元素
    }
}
使用方法等价使用方式所有权
for item in collectionfor item in IntoIterator::into_iter(collection)转移所有权
for item in &collectionfor item in collection.iter()不可变借用
for item in &mut collectionfor item in collection.iter_mut()可变借用

Match

if...else类似,但是必须写出所有条件下的执行代码

if...else不同的是:

  • match会在编译时检查错误
  • if...else则不会,只会在运行时报错
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(0..10);
    match num {
        1 => println!("first"),
        2 => println!("second"),
        3 => println!("third"),
        _ => println!("other"),
    }
}

可以用下划线_表示其它所有情况

tips:在 Rust 中 _ 的含义是忽略该值或者类型的意思,如果不使用 _,那么编译器会给你一个 变量未使用的 的警告。

2.3 函数

普通函数

从之前的示例代码可以看出来,fn就是定义函数的关键字

fn function(a:i32, b:i32) -> i32 {
    a + b
}

tips:最后一行不加;自动作为返回值返回

Closure 闭包

  • 函数中的函数
  • 匿名函数
  • lambda表达式
fn main() {
    let add = |a: i32, b: i32| -> i32 { a + b };
    let res = add(1, 2);
    println!("{}", res);
}

3.1 枚举 Enum

枚举可以是多个不同的枚举类型之一

主要作用:

  • 提高代码的可读性
  • 提供标识给编译器(提高性能)

系统的枚举

  • Result
  • Option

代码

// 便于打印
#[derive(Debug)]
enum Position {
    One,
    Two,
}
fn main() {
    // 打印复合类型
    println!("{:?}", Position::One);
}

3.2 结构体

  • rust中没有class

struct

  • 包含多段数据的类型
  • 每一段数据被称为field属性
  • 访问属性用.
#[derive(Debug)]
struct Rectangle {
    width: i32,
    height: i32,
}
fn main() {
    let rect = Rectangle {
        width: 5,
        height: 10,
    };
    println!("{}", rect.width);
    println!("{:#?}", rect);
}

struct 方法

  • 关联函数
  • 实例方法
  • 构造函数
  • Self
示例1
#[derive(Debug)]
struct Rectangle {
    width: i32,
    height: i32,
}

impl Rectangle {
    // 关联函数
    fn area(width: i32, height: i32) -> i32 {
        width * height
    }
    // 实例方法
    fn cal(&self) -> i32 {
        self.width * self.height
    }
    // 构造函数
    fn new(width: i32, height: i32) -> Self {
        Rectangle { width, height }
    }
}

fn main() {
    let rect = Rectangle {
        width: 5,
        height: 10,
    };
    let num1 = Rectangle::area(5, 5);
    println!("{}", num1);
    println!("{}", rect.cal());
    let rect2 = Rectangle::new(5, 5);
    println!("{:#?}", rect2);
}

示例2
#[derive(Debug)]
struct Person {
    name: String,
    age: i32,
}

impl Person {
    // 可能在外部调用,使用pub关键字
    pub fn new(name: String, age: i32) -> Self {
        Person { name, age }
    }
}

fn main() {
    let person1 = Person::new("Star-tears".to_string(), 18);
    println!("{:#?}", person1);
}

  • &self
  • &mut self
  • self
  • mut self
示例3
#[derive(Debug)]
struct Person {
    name: String,
    age: i32,
}

impl Person {
    // 可能在外部调用,使用pub关键字
    pub fn new(name: String, age: i32) -> Self {
        Person { name, age }
    }
    fn greet(&self) -> String {
        format!("Hi {}", self.name)
    }
    fn add_age(&mut self, n: i32) {
        self.age += n;
    }
}

fn main() {
    let mut person1 = Person::new("Star-tears".to_string(), 18);
    println!("{:#?}", person1);
    println!("{}", person1.greet());
    person1.add_age(3);
    println!("{}", person1.age);
}

&为引用,但默认是不可变的,如果希望可变,需要加mut关键字

3.3 Tuples 元组

  • 相似类型有数组、切片,但在rust中不常用,常用Vector

元组

  • 匿名存储数据
  • 不可变

元组的用处

  • 函数返回
  • 提取变量

代码

fn a_tuple() -> (i32, i32) {
    (0, 1)
}

fn main() {
    println!("{:?}", a_tuple());
    let (a, b) = a_tuple();
    println!("a: {}, b: {}", a, b);
}

4.1 所有权机制(OwnerShip)

OwnerShip Model

OwnerShip规则
  • 每个值都有一个变量称为所有者
  • 每个值只能有一个所有者
  • 当所有者超出作用域时,值被销毁
Stack 和 Heap
  • Stack存储已知大小的数据 快 基础数据类型等
  • Heap存储未知大小的数据 慢 Struct等
move

浅拷贝加失效

fn main() {
    let a = "abc".to_string();
    let b = a; // a失效
}

其它语言在这样子的赋值时大多数只做一个浅拷贝,但在rust里,它是把所有权交给b,将a失效

clone
  • 深度拷贝
  • 开辟新的空间
fn main() {
    let a = "abc";
    let b = a;
    println!("{}, {}", a, b);
}

输出如下:

    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target\debug\hello.exe`
abc, abc

引用

reference
  • 通过&可以获得值的引用
  • 未获得值的所有权,那么它就不会在作用域结束后被销毁
  • 这种情况叫借用borrowed
struct A {
    a: i32,
}
fn change_a(a: &mut A) {
    a.a = 233;
    println!("{}", a.a);
}

fn main() {
    let mut a = A { a: 1 };
    println!("{}", a.a);
    change_a(&mut a);
    println!("{}", a.a);
}

注意

在同一作用域下,对某一块数据:

  1. 可以有多个不可变引用
  2. 只能有一个可变的引用
  3. 不能同时拥有一个可变引用和一个不可变引用

Copy 和 Clone

这两个都是特质(trait)

  • Copy trait
  • Clone trait
#[derive(Debug, Clone, Copy)]
struct Person<'a> {
    name: &'a str,
    age: i32,
}

fn main() {
    let person = Person {
        name: "Star-tears",
        age: 18,
    };
    let pp = person;
    println!("{:?}", person);
}

tips:手动标注name作用域与结构体实例作用域一致

4.2 生命周期 lifetime

生命周期

  • 避免dangling referfence(避免悬垂引用)
  • rust中所有的引用都有自己的生命周期,表示引用有效的作用域
  • 一般为隐式的,但不可推断时会报错,需要手动标注生命周期

手动标识生命周期

  • fn longest(x: &str, y: &str) -> &str
    
  • 生命周期最短的为有效

  • 尽量不要用'static

  • 省略规则

    • 每个引用类型的参数都有自己的生命周期
    • 只有一个输入生命周期参数,那么输出生命周期就为该生命周期
    • &self&mut self,那么输出生命周期就为该self生命周期
  • 生命周期机制还在完善

5.1 常用数据类型

String

  • &str: String slices 不可变的

    • let ss = "ss";
      
  • String objects

    • let mut s = String::new();
      
    • let s = String::from("star");
      
    • let s =  "s".to_string();
      
    • 通过format!生成

      let s = format!("{}", 233);
      
String methods
  • len()
  • push 字符
  • push_str 字符串
  • replace

Vector

构造
let mut arr = Vec::new();
let arr = vec![1,2,3];
let arr = vec![1;20];
push 和 remove
arr.push(2);
arr.remove(0);
更新
num[3] = 5;
num.get(3)
fn main() {
    let mut num: Vec<i32> = vec![0; 20];
    num.push(1);
    match num.get(20) {
        Some(n) => println!("{}", n),
        None => {}
    }
}
遍历
  • iter
fn main() {
    let mut num: Vec<i32> = vec![0; 20];
    num.push(1);
    for it in num.iter() {
        println!("{}", it);
    }
}

5.2 HashMap

  • 散列函数

    • 散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫散列值(hash values, hash codes, hash sums, 或hashes)的指纹
  • HashMap由链表加数组组成,它的底层结构是一个数组,而数组的元素是一个单向链表

  • HashMap 的键可以是布尔型、整型、字符串,或者任意实现了 Eq 和 Hash trait 的其他类型

  • HashMap 也是可增长的,但 HashMap 在占据了多余空间时还可以缩小自己

use std::collections::HashMap;

fn main() {
    let mut str_map = HashMap::new();
    str_map.insert("star", 18);
    str_map.insert("tears", 19);
    println!("{:#?}", str_map);
    match str_map.get(&"star") {
        Some(v) => println!("{:#?}", v),
        _ => {}
    }
}

6.1 特质和泛型(Trait && generics)

Traits

  • 与接口和抽象类类似
  • 给结构体添加定义的行为
impl trait_demo for struct_demo
struct Steve {
    name: String,
}

impl Person for Steve {
    fn new(name_str: String) -> Self {
        Steve { name: name_str }
    }

    fn language(&self) -> &str {
        "Steve"
    }
}

struct Alex {
    name: String,
}

impl Person for Alex {
    fn new(name_str: String) -> Self {
        Alex { name: name_str }
    }

    fn language(&self) -> &str {
        "Alex"
    }
    fn eat_food(&self) {
        println!("Eat fish!");
    }
}

trait Person {
    fn new(name_str: String) -> Self;
    fn language(&self) -> &str;
    fn eat_food(&self) {
        println!("Eat food");
    }
}

fn main() {
    let a = Steve::new("Star_tears".to_string());
    let b = Alex::new("T_dream".to_string());
    a.eat_food();
    b.eat_food();
}

  • 返回枚举

    • fn get_trait -> Box<dyn trait_name>
      

泛型

  • 泛型程序设计(generic programming)是程序设计语言的一种风格或范式
  • 泛型运行程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型
trait Bark {
    fn bark(&self) -> String;
}

struct Dog {
    species: String,
}

struct Cat {
    color: String,
}

impl Bark for Dog {
    fn bark(&self) -> String {
        format!("{} barking", self.species)
    }
}

fn bark<T: Bark>(b: T) {
    println!("{}", b.bark());
}

fn main() {
    let dog = Dog {
        species: "white dog".to_string(),
    };
    bark(dog);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值