2024 Rust现代实用教程:Ownership与结构体、枚举

文章目录

一、Rust的内存管理模型

Rust

  • 所有权机制
  • 引用称之为借用

1.GC(Stop the world)

"Stop the world"是与垃圾回收(Garbage Col lection)相关的术语,它指的是在进行垃圾回收时系统暂停程序的运行。

这个术语主要用于描述一种全局性的暂停,即所有应用线程都被停止,以便垃圾回收器能够安全地进行工作。这种全局性的停止会导致一些潜在的问题,特别是对于需要低延迟和高性能的应用程序。

需要注意的是,并非所有的垃圾回收算法都需要"stop the world'",有一些现代的垃圾回收器采用了一些技术来减小全局停顿的影响,比如并发垃圾回收和增量垃圾回收。

2.C/C++内存错误大全

cpp 复制代码
1.内存泄漏(Memory Leaks)
int*ptr new int;
//忘记释放内存
/delete ptr:

2.悬空指针(Dangl ing Pointers)
int*ptr new int;
delete ptr;//ptr现在是悬空指针

3。重复释放(Double Free)
int*ptr new int;
delete ptr;
delete ptr;

4.数组越界(Array Out of Bounds
int arr [5];
arr[5]=10:

5.野指针(Memory Leaks)
int*ptr;
*ptr=10;/野指针

6.使用已经释放的内存(Use After Free)
int*ptr new int;
delete ptr;
*ptr=10;//使用已释放的内存

·7.堆栈溢出(Stack0 verflow)
递归堆栈溢出

·8.不匹配的new/delete或malloc,free

3.Rust的内存管理模型

所有权系统(Ownership System)

借用(Bor rowing)

  • 不可变引用(不可变借用)
  • 可变引用(可变借用)

生命周期(Lifetimes)

引数计数(Reference Counting)

eg:

rust 复制代码
fn get_length(s: String) -> usize {
    println!("String: {}", s);
    // 函数结束之后 main::s1也销毁了
    s.len()
}
fn main() {
    // copy  move
    // copy
    let c1 = 1;
    let c2 = c1;
    println!("{}", c1);
    let s1 = String::from("value");
    // let s2 = s1;//所有权转移
    let s2 = s1.clone(); // 深拷贝
                         //  println!("{s1}"); //  value borrowed here after move
    println!("{}", s1);

    let len = get_length(s1);
    println!("{}", len);

    let back = first_word("hello world");
    println!("{}", back);
    let back = first_word("we are the world");
    println!("{}", back);
}

fn dangle() -> String {
    "hello".to_owned()
}

// 静态的声明周期
// 不建议使用
fn dangle_static() -> &'static str {
    "jdkfj"
}

// 传进来是引用,传出去也是引用
// String 与 &str,实际上就是vec u8的ref
fn first_word(s: &str) -> &str {
    let bytes: &[u8] = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..] //直接返回s也可以
}

编译以及运行

bash 复制代码
 cargo run
   Compiling ch5_ownership_introduce v0.1.0 (/home/wangji/installer/rust/project/ch5_ownership_introduce)
warning: unused variable: `c2`
  --> src/main.rs:10:9
   |
10 |     let c2 = c1;
   |         ^^ help: if this is intentional, prefix it with an underscore: `_c2`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `s2`
  --> src/main.rs:14:9
   |
14 |     let s2 = s1.clone(); // 深拷贝
   |         ^^ help: if this is intentional, prefix it with an underscore: `_s2`

warning: function `dangle` is never used
  --> src/main.rs:27:4
   |
27 | fn dangle() -> String {
   |    ^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: function `dangle_static` is never used
  --> src/main.rs:33:4
   |
33 | fn dangle_static() -> &'static str {
   |    ^^^^^^^^^^^^^

warning: `ch5_ownership_introduce` (bin "ch5_ownership_introduce") generated 4 warnings (run `cargo fix --bin "ch5_ownership_introduce"` to apply 2 suggestions)
    Finished dev [unoptimized + debuginfo] target(s) in 0.37s
     Running `target/debug/ch5_ownership_introduce`
1
value
String: value
5
hello
we

二、String与&str

String,是一个堆分配的可变字符串类型

  • 源码:
rust 复制代码
pub struct String{
	vec:Vec<u8>,
}
  • u8的可变数组

&str是指字符串切片引用,是在栈上分配的

  • 字符串字面量
  • 不可变引用,指向存储在其他地方的UTF-8编码的字符串数据
  • 由指针和长度构成(由C++的角度去看)

1.String与&str如何选择

注意String是具有所有权的,而&str并没有

Struct中属性使用String

  • 如果不使用显式声明生命周期,则无法使用&str
  • 不只是麻烦,还有更多的隐患

函数参数推荐使用&str(如果不想交出所有权)

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

2.Example

rust 复制代码
struct Person<'a> {
    name: &'a str, //'a的函数是name的生命周期和Person是一样的
    color: String,
    age: i32,
}

struct P2Persion {
    name: String,
    color: String,
    age: i32,
}

//===================String 和 &str测试===================
// 可以传递的入参类型:&String, &str
fn print(data: &str) {
    println!("{}", data);
}

//===================String 和 &str测试===================
// 可以传递的入参类型是:&String
fn print_string_borrow(data: &String) {
    println!("{}", data);
}

fn main() {
    //===================String 和 &str测试===================
    // 从字面量&str转成String的三种方法:(1)String::from(2)to_string()(3)to_owned()
    let name = String::from("Value C++");
    let name_2 = "wangji".to_owned();
    let course = "Rust".to_string();

    // String有很多方法
    let new_name = name.replace("C++", "CPP");
    println!("{name_2} {name} {course} {new_name}");

    //&str 能够直接进行ASICC编码
    let rust = "\x52\x75\x73\x74"; // ascii
    println!("{rust}");

    //===================结构体测试===================
    let pp = P2Persion {
        name: "value".to_string(),
        color: "green".to_string(),
        age: 89,
    };

    //String &str
    let color = "green".to_string();
    let name = "John";
    let people = Person {
        name: name,
        color: color,
        age: 89,
    };

    // func
    let value = "value".to_owned();
    print(&value);
    print("data");
    // print_string_borrow("dd");
    print_string_borrow(&value);
}

编译及运行

bash 复制代码
 cargo run
warning: unused variable: `pp`
  --> src/main.rs:41:9
   |
41 |     let pp = P2Persion {
   |         ^^ help: if this is intentional, prefix it with an underscore: `_pp`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `people`
  --> src/main.rs:50:9
   |
50 |     let people = Person {
   |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_people`

warning: fields `name`, `color`, and `age` are never read
 --> src/main.rs:2:5
  |
1 | struct Person<'a> {
  |        ------ fields in this struct
2 |     name: &'a str, //'a的函数是name的生命周期和Person是一样的
  |     ^^^^
3 |     color: String,
  |     ^^^^^
4 |     age: i32,
  |     ^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: fields `name`, `color`, and `age` are never read
  --> src/main.rs:8:5
   |
7  | struct P2Persion {
   |        --------- fields in this struct
8  |     name: String,
   |     ^^^^
9  |     color: String,
   |     ^^^^^
10 |     age: i32,
   |     ^^^

warning: `ch2_string_str` (bin "ch2_string_str") generated 4 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/ch2_string_str`
wangji Value C++ Rust Value CPP
Rust
value
data
value

三、枚举与匹配模式

枚举(enums)是一种用户自定义的数据类型,用于表示具有一组离散可能值的变量

  • 每种可能值都称为"variant"(变体)
  • 枚举名:变体名

枚举的好处

  • 可以使你的代码更严谨、更易读
  • More robust programs
rust 复制代码
enum Shape{
Circle(f64)
Rectangle(f64,f64),
Square (f64)
}

1.常见的枚举类型:Option和Result

rust 复制代码
pub enum Option<T>{
	None,
	Some (T),
}

pub enum Result<T,E>{
	Ok(T)
	Err (E),
}

2.匹配模式

1.match关键字实现

2.必须覆盖所有的变体

3.可以用_、,...=、三元(if)等来进行匹配

rust 复制代码
match number{
	0 => printIn!("Zero"),
	1|2 => printIn!("One or Two"),
	3..=9 => printIn!("From Three to Nine"),
	n if n%2 ==0 =>printIn!("Even number"),
	_=printIn!("Other"),
}

eg:

rust 复制代码
use std::collections::btree_set::Union;

enum Color {
    Red,
    Yellow,
    Blue,
}

fn print_color(my_color: Color) {
    match my_color {
        Color::Red => println!("Red"),
        Color::Yellow => println!("Yellow"),
        Color::Blue => println!("Blue"),
    }
}

enum BuildingLocation {
    Number(i32),
    Name(String), // 不用&str
    Unknown,
}

// rust是面向函数,面向行为
// rust:不能把行为写在struct里面,与C++不同
impl BuildingLocation {
    fn print_location(&self) {
        match self {
            // BuildingLocation::Number(44)
            BuildingLocation::Number(c) => println!("building number {}", c),
            // BuildingLocation::Name("ok".to_string())
            BuildingLocation::Name(s) => println!("building name {}", *s),
            BuildingLocation::Unknown => println!("unknown"),
        }
    }
}

fn main() {
    let a = Color::Red;
    print_color(a);
    // let b = a;所有权已经转移到print_color里面了

    let house = BuildingLocation::Name("fdfd".to_string());
    house.print_location();
    let house = BuildingLocation::Number(1);
    house.print_location();
    let house = BuildingLocation::Unknown;
    house.print_location();
}

编译及运行:

bash 复制代码
 cargo run
   Compiling ch7_enum v0.1.0 (/home/wangji/installer/rust/project/ch7_enum)
warning: unused import: `std::collections::btree_set::Union`
 --> src/main.rs:1:5
  |
1 | use std::collections::btree_set::Union;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: variants `Yellow` and `Blue` are never constructed
 --> src/main.rs:5:5
  |
3 | enum Color {
  |      ----- variants in this enum
4 |     Red,
5 |     Yellow,
  |     ^^^^^^
6 |     Blue,
  |     ^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `ch7_enum` (bin "ch7_enum") generated 2 warnings (run `cargo fix --bin "ch7_enum"` to apply 1 suggestion)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 6.46s
     Running `target/debug/ch7_enum`
Red
building name fdfd
building number 1
unknown

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

结构体间接

  • 结构体是一种用户定义的数据类型,用于创建自定义的数据结构。
rust 复制代码
struct Point{
x:i32,
y:i32,
}
  • 每条数据(x和y)称为属性(字段field)
  • 通过点(.)来访问结构体中的属性

结构体中的方法

  • 在rust中都称之为关联函数,这里为了方便称之为方法了
  • 这里的方法是指,通过实例调用(&self、&mut self、self)
  • &self。引用
  • &mut self,可变self的引用
  • self,交出所有权
rust 复制代码
impl Point
fn distance (&self,other:&Point)->f64{
	let dx = (self.x - other.x)as f64;
	let dy = (self.y - other.y)as f64;
	(dx*dx + dy*dy).sqrt()
}

结构体中的关联函数

  • 关联函数是与类型相关的函数,调用时为结构体名::函数名
  • Self 就是Point,结构体的名字
rust 复制代码
impl Point{
	fn new(x: u32, y:u32) ->self{
		Point{x,y}
	}
}

结构体中的关联变量

  • 这里的关联变量是指,和结构体类型相关的变量,也可以在特质或是枚举中
rust 复制代码
impl Point{
	const Pl:f64=3.14;
}
  • 调用时使用Point::Pl

eg:

rust 复制代码
enum Flavor {
    Spicy,
    Sweet,
    Fruity,
}

struct Drink {
    flavor: Flavor,
    price: f64,
}

impl Drink {
    // 关联变量
    const MAX_PRICE: f64 = 10.0;
    // 方法,&self不能修改里面的成员
    // Drink::MAX_PRICE,等价写法Self::MAX_PRICE
    fn buy(&self) {
        if self.price > Drink::MAX_PRICE {
            println!("I am poor");
            return;
        }
        println!("buy it");
    }
    // 关联函数
    // Self表示返回Drink结构体
    fn new(price: f64) -> Self {
        Drink {
            flavor: Flavor::Fruity,
            price,
        }
    }
}

fn print_drink(drink: Drink) {
    match drink.flavor {
        Flavor::Fruity => println!("fruity"),
        Flavor::Spicy => println!("spicy"),
        Flavor::Sweet => println!("sweet"),
    }
    println!("{}", drink.price);
}

fn main() {
    let sweet = Drink {
        flavor: Flavor::Sweet,
        price: 6.0,
    };
    println!("{}", sweet.price);
    print_drink(sweet); // sweet 已经被销毁了
                        // print_drink(sweet);

    let sweet = Drink::new(12.0);
    sweet.buy();
}

编译及运行:

bash 复制代码
 cargo run
   Compiling ch8_struct v0.1.0 (/home/wangji/installer/rust/project/ch8_struct)
warning: variant `Spicy` is never constructed
 --> src/main.rs:2:5
  |
1 | enum Flavor {
  |      ------ variant in this enum
2 |     Spicy,
  |     ^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `ch8_struct` (bin "ch8_struct") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/ch8_struct`
6
sweet
6
I am poor

五、Ownership与结构体

Ownership Rules

  • 1.Each value in Rust has an owner
  • 2.There can only be one owner at a time
  • 3.Values are automatically dropped when the owner goes out of scope

Value Passing Semantics值传递语义

每当将值从一个位置传递到另一个位置时,borrow checker:都会重新评估所有权。

  • 1.Immutab|e Borrow使用不可变的借用,值的所有权仍归发送方所有,接收方直接接收对该值的引用,而不是该值的副本。但是,他们不能使用该引用来修改它指向的值,编译器不允许这样做。释放资源的责任仍由发送方承担。仅当发件人本身超出范围时,才会删除该值
  • 2.Mutable Borrow使用可变的借用所有权和删除值的责任也由发送者承担。但是接收方能够通过他们接收的引用来修改该值。
  • 3.Move这是所有权从一个地点转移到另一个地点。borrow checker关于释放该值的决定将由该值的接收者(而不是发送者)通知。由于所有权已从发送方转移到接收方,因此发送方在将引用移动到另一个上下文后不能再使用该引用,发送方在移动后对v|aue的任何使用都会导致错误。

结构体中关联函数的参数

rust 复制代码
&self 
对应的,在函数参数中:(self:&Self)
不可变引用

&mut self 
对应的,在函数参数中(self:&mut Self)
可变引用

self 
对应的,在函数参数中(self:Self)
Move

impl Point{
get (self:Self)->i32{
	self.x
}
}

Example:

rust 复制代码
struct Counter {
    number: i32,
}

impl Counter {
    fn new(number: i32) -> Self {
        Self { number }
    }

    // 不可变借用
    fn get_number(&self) -> i32 {
        self.number
    } // Counter::get_number(self: &Self)
      // 不可变借用
    fn add(&mut self, increment: i32) {
        self.number += increment;
    } // Counter::add(self: &mut Self, increment:)
      // move
    fn give_up(self) {
        println!("free {}", self.number);
    }

    /**
    等价于下面的写法
    fn combine(c1: Self, c2: Self) -> Self {
        return Self {
            number: c1.number + c2.number,
        };
    }
     */
    fn combine(c1: Self, c2: Self) -> Self {
        Self {
            number: c1.number + c2.number,
        }
    }
}

fn main() {
    let mut c1 = Counter::new(0);
    println!("c1 number {}", c1.get_number());
    println!("c1 number {}", c1.get_number());
    c1.add(2);
    println!("c1 number {}", c1.get_number());
    println!("c1 number {}", c1.get_number());
    c1.give_up();
    // println!("c1 number {}", c1.get_number());

    let c1 = Counter::new(2);
    let c2 = Counter::new(1);
    let c3 = Counter::combine(c1, c2);
    // println!("c1 number {}", c1.get_number());
    // println!("c2 number {}", c2.get_number());

    println!("c3 number {}", c3.get_number());
}

编译及运行:

bash 复制代码
 cargo run 
   Compiling ch9_ownership_struct v0.1.0 (/home/wangji/installer/rust/project/ch9_ownership_struct)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.31s
     Running `target/debug/ch9_ownership_struct`
c1 number 0
c1 number 0
c1 number 2
c1 number 2
free 2
c3 number 3

五、堆与栈、Copy与Move

1.堆与栈

stack

  • 1.堆栈将按照获取值的顺序存储值,并以相反的顺序删除值
  • 2.操作高效,函数作用域就是在栈上
  • 3.堆栈上存储的所有数据都必须具有已知的固定大小数据

heap

  • 1.堆的规律性较差,当你把一些东西放到你请求的堆上时,你请求,请求空间,并返回一个指针,这是该位置的地址
  • 2.长度不确定

2.Box智能指针

Box是一个智能指针,它提供对堆分配内存的所有权。它允许你将数据存储在堆上而不是栈上,并且在复制或移动时保持对数据的唯一拥有权。使用Bo×可以避免一些内存管理问题,如悬垂指针和重复释放。

  • 1.所有权转移
  • 2.释放内存
  • 3.解引用
  • 4.构建递归数据结构

3.Copy与Move

Move:所有权转移

Clone:深拷贝

Copy:Copy是在CIone的基础建立的marker trait(Rust中最类似继承的关系)

  • 1.trait(特质)是一种定义共享行为的机制。Clone也是特质
  • 2 marker trait(标记特质)是一个没有任何方法的trait,他主要用于向编译期传递某些信息,以改变类型的默认行为

4.小结

stack

  • 1.基础类型
  • 2.tuple和array
  • 3.struct与枚举等也是存储在栈上,如果属性有String等在堆上的数据类型会有指向堆的

heap

  • Box Rc String/Vec等

一般来说在栈上的数据类型都默认是copy的,但是struct等默认为move,需要Copy只需要设置数据类型实现Copy特质即可,或是调用CIone函数(需要实现CIone特质)

box Example:

rust 复制代码
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let boxed_point = Box::new(Point { x: 10, y: 20 });
    println!("x:{}, y:{}", boxed_point.x, boxed_point.y);

    let mut boxed_point = Box::new(32);
    println!("{}", *boxed_point);
    *boxed_point += 10;
    println!("{}", *boxed_point);
}

编译及运行:

bash 复制代码
 cargo run
   Compiling ch10_box v0.1.0 (/home/wangji/installer/rust/project/ch10_box)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.43s
     Running `target/debug/ch10_box`
x:10, y:20
32
42

copy and move Example:

rust 复制代码
#[derive(Debug, Clone, Copy)]
struct Book {
    page: i32,
    rating: f64,
}

fn main() {
    let x = vec![1, 2, 3, 4];
    let y = x.clone();
    println!("{:#?}", y);
    println!("{:?}", x);

    let x = "ss".to_string();
    let y = x.clone();
    println!("{:?}", x);

    let b1 = Book {
        page: 1,
        rating: 0.1,
    };
    let b2 = b1; // copy
    println!("{}", b1.page);
    println!("{:?}", b2);
}

编译及运行:

bash 复制代码
 cargo run
warning: unused variable: `y`
  --> src/main.rs:14:9
   |
14 |     let y = x.clone();
   |         ^ help: if this is intentional, prefix it with an underscore: `_y`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: field `rating` is never read
 --> src/main.rs:4:5
  |
2 | struct Book {
  |        ---- field in this struct
3 |     page: i32,
4 |     rating: f64,
  |     ^^^^^^
  |
  = note: `Book` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
  = note: `#[warn(dead_code)]` on by default

warning: `ch10_copy_move` (bin "ch10_copy_move") generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/ch10_copy_move`
[
    1,
    2,
    3,
    4,
]
[1, 2, 3, 4]
"ss"
1
Book { page: 1, rating: 0.1 }

参考

相关推荐
yuanbenshidiaos几秒前
C++-----函数与库
开发语言·c++·算法
1 9 J3 分钟前
Java 上机实践11(组件及事件处理)
java·开发语言·学习·算法
wzg201618 分钟前
python装饰器
开发语言·python
十二测试录18 分钟前
Python基础——字符串
开发语言·经验分享·python·程序人生·职场发展
一條狗1 小时前
隨筆 20241224 ts寫入excel表
开发语言·前端·typescript
Takumilove1 小时前
MQTT入门:在Spring Boot中建立连接及测试
java·spring boot·后端
yayaya1521 小时前
javaScriptBOM
开发语言·javascript·ecmascript
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
C#都可以找哪些工作?
开发语言·c#
凡人的AI工具箱1 小时前
每天40分玩转Django:Django管理界面
开发语言·数据库·后端·python·django
cloud___fly2 小时前
Spring AOP入门
java·后端·spring