自学内容网 自学内容网

Rust简明教程第三章-所有权与借用

观看B站软件工艺师杨旭的rust教程学习记录,有删减有补充

Stack&Heap 栈内存和堆内存

  • stack用于存放函数的参数、局部变量等
  • stack只能从栈顶操作数据,压栈push,出栈pop,后进先出
  • stack存储的数据必须有已知的固定大小
  • stack写入读取数据比stack快得多
  • heap存储编译时大小未知或运行时可能发生变化的数据
  • heap分配空间先标记一块内存空间为在用,并返回一个指向这块地址的指针
  • heap分配内存返回的指针大小已知,heap的指针存储在stack

所有权

  • 每个值都有一个变量,这个变量是该值的所有者
  • 每个值同时只能有一个所有者
  • 当所有者超出作用域(scope)时,该值将被删除

字符串字面值,大小已知,存储在stack上

fn main(){
    //s不可用
    let s = "Hello";//s可用

}//s作用域结束,s不可用

integerarray等大小固定的值存储在stack上,而String类型的值数据存储在heap上,String类型的值是可变的,地址存储在stack

fn main() {
    let mut s = String::from("Hello");
    s.push_str(",World");
    println!("{}", s); //Hello,World
}//s离开作用域调用drop

Move 变量和数据的交互方式

Rust处理资源时,默认行为是move(转移/移动)

对于已知固定大小的值,move的方式是在stack上复制stack上存储的值并绑定到新声明的y变量

fn main() {
    let x = 5;//x拥有5
    let y = a;//y拥有copy的5
    println!("{}", x); //5
    println!("{}", y); //5
}

对于可变大小的值,move的方式是在stack上复制stack存储的指向heap的指针(、长度、容量),让原来绑定指针的变量失效

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1;//s1不再拥有"Hello",所有权move到s2
    // println!("{}",s1);//s1失效,s1不可用
    println!("{}",s2);//Hello
}

Shallow copy&Deep copy 浅拷贝和深拷贝

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存(开销小)
  • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(开销大)

Rust对存储在heap上的数据类型不仅复制了指针还让原来的变量失效,所以称作Move(移动),基础类型的值存储在satck中直接复制和复制指针没有太大的性能差距,而存储在heap上的复杂类型直接复制值会消耗大量性能

如果要实现对heap上的数据深拷贝,使用clone()方法,s1和s2所有权没有改变,s2拥有的是被clone的新数据的所有权

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1.clone();
    println!("{}", s1);//Hello
    println!("{}", s2);//Hello
}

Copy trait 复制特质

  • 对于已知固定大小的数据类型,数据完全存储在stack中的类型,rust默认实现了Copy trait
  • 如果一个类型实现了Copy trait,那么变量绑定实际上是复制实际数据,旧变量依然可用
  • 如果一个类型或者该类型的一部分实现了Copy trait,那么Rust不允许再让它实现Copy trait
  • 任何简单标量的组合类型都实现了Copy trait
  • 任何需要分配内存或者某种资源的都不是Copy的

如:

integer:u8i32
bool
char
float:f32f64
Tuple:如果其中所有字段都是固定大小的可以Copy的,这个元组也是可以Copy

所有权与函数

fn main(){
    let  s = String::from("Hello World");
    take_ownership(s);//move所有权,s失效,不可用
    let x= 5;
    makes_copy(x);//copy, 传入的是copy的值,不改变所有权
    println!("x:{}",x);//x:5

}
fn take_oenership(some_string:String){
    println!("{}",some_string);//Hello World
}//drop s
fn makes_copy(some_number:i32){
    println!("{}",some_number);//5
}

返回值与作用域

函数在返回值的过程中也会发生所有权转移

fn main() {
    let s1 = gives_ownership();//将world所有权move到s1
    println!("{}",s1);//world
    let s2 = String::from("hello");//变量s2绑定hello的所有权
    let s3 = takes_and_gives_back(s2);//将函数中返回的所有权move到s3,s2失效
    println!("{}",s3);//hello
}//drop s1、s3
fn gives_ownership() -> String {
    let some_string = String::from("world");//some_string绑定world的所有权
    some_string//返回world所有权
}
fn takes_and_gives_back(a_string: String) -> String {//s2的所有权move到函数中
    a_string//返回s2的所有权
}
  • 把一个值绑定给其他变量时就会发生move
  • 当一个包含heap数据的变量离开作用域时,它的值会被drop函数清理,除非数据的所有权move到另一个变量上了(包含heap数据的变量被move到另一个变量后原变量会失效,所以不用drop)

引用与借用

函数使用值但不获得所有权

fn main() {
    let s1 = String::from("hello"); //将hello绑定到s1
    let len = calculate_length(&s1); //引用s1但不move s1,move的是s1的引用
    println!("{}的长度为:{}", s1, len); //hello的长度为:5
}
fn calculate_length(s: &String) -> usize {//借用s1不获得所有权
    s.len() //返回s1的长度
}
  • 把引用作为函数参数叫借用

修改借用的值,借用时&mut

fn main() {
    let mut s1 = String::from("hello"); //将hello绑定到s1
    let len = calculate_length(&mut s1); //引用s1但不move s1,move的是s1的引用
    println!("{}的长度为:{}", s1, len); //hello,world的长度为:11
}
fn calculate_length(s:&mut String) -> usize {//借用s1不获得所有权
    s.push_str(",world");
    s.len() //返回s1的长度
}
  • 可变引用在同一作用域只能借用1次(避免数据竞争)
  • 不可变引用在同一作用域可以借用多次
  • 不可以在同一作用域同时借用一个可变引用和一个不可变引用

使用{}创建非同一作用域的可变引用

fn main() {
    let mut s = String::from("hello");
    {
        let s1 = &mut s; //借用可变引用s
        print_info(s1); //hello
    }
    let s2 = &mut s;//借用可变引用s
    print_info(s2); //hello
}
fn print_info(s: &mut String) {
    println!("{}", s);
}

slice切片

&str 字符串切片(String类型)

  • 字符串切片的范围索引必须发生在有效的UTF-8字符边界内
  • slice是对String类型数据的借用,不获得所有权
fn main() {
    let s = String::from("hello world");
    //slice
    let hello = &s[..5];
    let world = &s[6..11]; //或[6..]或[6..s.len()]
    println!("{},{}", hello, world); //hello,world
    //整个也是切片
    let whole = &s[0..s.len()]; //或[..]
    println!("{}", whole); //hello world
}

fn first_word(s: &str) -> &str{}

  • 使用在字符串切片直接调用该函数
  • 使用String类型,可以借用一个完整的切片调用该函数(函数可以被切片类型复用)
fn main() {
    let a_string = String::from("hello world");//String类型
    let wordIndex = first_world(&a_string[..]);//字符串切片
    println!("{}", wordIndex);//hello
    let b_string = "hello rust";//本质是切片&str
    let wordIndex = first_world(b_string);
    println!("{}", wordIndex);//hello
}
//遇到空格切片
fn first_world(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    &s[..]
}

Array slice 数组切片

fn main(){
    let arry=[1,2,3,4,5,6];
    let slice = &arry[0..5];
    println!("{:?}",slice);//[1, 2, 3, 4, 5]
}

还可以对切片切片

fn main() {
    let arry = [1, 2, 3, 4, 5, 6];
    let slice = &arry[0..5];
    println!("{:?}", slice); //[1, 2, 3, 4, 5]

    let slice_of_slice = &slice[..2];
    println!("{:?}", slice_of_slice); //[1, 2]
}

Struct 结构体

  • struct是自定义的数据类型
  • 变量绑定顺序和声明的顺序可以不一样
#[derive(Debug)] //派生属性,Debug用来格式化输出值,顾名思义,debug用的
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let user1 = User {
        email: String::from("abc@outlook.com"),
        username: String::from("张三"),
        active: true,
        sign_in_count: 455,
    };
    println!("{:?}", user1); //User { username: "张三", email: "abc@outlook.com", sign_in_count: 455, active: true }
}
  • 一旦struct的实例是可变的,那么实例中的所有字段都是可变的
#[derive(Debug)] //派生属性,Debug用来格式化输出值,顾名思义,debug用的
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let mut user1 = User {
        email: String::from("abc@outlook.com"),
        username: String::from("张三"),
        active: true,
        sign_in_count: 455,
    };//绑定获得所有权
    //修改值
    user1.username = String::from("李四");//变量绑定新值所有权
    user1.active = false;
    println!("{:?}", user1); //User { username: "李四", email: "abc@outlook.com", sign_in_count: 455, active: false }
}//drop user1

字段和参数同名可简写

#[derive(Debug)] //派生属性,Debug用来格式化输出值,顾名思义,debug用的
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let user2 = User {
        email: String::from("abc@gmail.com"),
        username: String::from("张三"),
        active: true,
        sign_in_count: 000,
    };
    println!("{:?}", user2); //User { username: "张三", email: "abc@gmail.com", sign_in_count: 0, active: true }
}
fn build_user(email: String, username: String) -> User {
    User {
        email,    //等价于email:email,
        username, //等价于username:username,
        active: false,
        sign_in_count: 455,
    }
}

struct更新语法,创建部分字段相同的新实例,只需要更新不同的字段

#[derive(Debug)] //派生属性,Debug用来格式化输出值,顾名思义,debug用的
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let user2 = User {
        email: String::from("abc@gmail.com"),
        username: String::from("张三"),
        active: true,
        sign_in_count: 000,
    };
    println!("{:?}", user2); //User { username: "张三", email: "abc@gmail.com", sign_in_count: 0, active: true }

    let user3 = User {
        username: String::from("王五"),
        ..user2
    };
    println!("{:?}", user3); //User { username: "王五", email: "abc@gmail.com", sign_in_count: 0, active: true }
}
fn build_user(email: String, username: String) -> User {
    User {
        email,    //等价于email:email,
        username, //等价于username:username,
        active: false,
        sign_in_count: 455,
    }
}

Tuple struct 元组结构体

  • Tuple struct整体有名,字段无名
struct Color_RGB(u8,u8,u8)
fn main(){
    let blue = Color_RGB(0,111,255);
}

Unit-Like struct 无字段结构体

()单元类型相似

struct Marker;

结构体与所有权

#[derive(Debug)] //派生属性,Debug用来格式化输出值,顾名思义,debug用的
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    }; //变量绑定
    println!("{}", area(&rect)); //1500
    println!("{:#?}",&rect);//Rectangle { width: 30, height: 50 }
}
fn area(rect: &Rectangle) -> u32 {//借用所有权
    rect.width * rect.height
}

解构

tuple解构

fn main() {
    let tuple = (1, "hello", 3.5);
    // 解构元组
    let (x, y, z) = tuple;
    println!("x: {}, y: {}, z: {}", x, y, z); //x: 1, y: hello, z: 3.5
}

struct解构

struct Point {
    x: i32,
    y: i32,
}
fn main() {
    let point = Point { x: 10, y: 20 };
    // 解构结构体
    let Point { x, y } = point;
    println!("x: {}, y: {}", x, y);//x: 10, y: 20
}

enum解构

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
fn main() {
    let msg1 = Message::Move { x: 10, y: 20 };
    let msg2 = Message::Write(String::from("Hello, Rust!"));
    let msg3 = Message::ChangeColor(255, 0, 0);
    let msg4 = Message::Quit;

    match msg1 {
        Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
        _ => (),//匹配失败返回单元类型
    }
    match msg2 {
        Message::Write(text) => println!("Write message: {}", text),
        _ => (),
    }
    match msg3 {
        Message::ChangeColor(r, g, b) => println!("Change color to r: {}, g: {}, b: {}", r, g, b),
        _ => (),
    }
    match msg4 {
        Message::Quit => println!("Quit message received"),
        _ => (),
    }
}

方法

  • 方法和函数类似:fn关键字、函数名、参数、返回值
  • 方法在struct(或enum、trait对象)的上下文定义
  • 方法第一个参数是(&self),表示调用方法的实例,也可以是可变借用(&mut self),也可以不使用借用直接获得所有权(self)
  • impl块里定义方法
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn area(&self) -> u32 {//借用调用该方法的实例的所有权
        self.width * self.height
    }
}
fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    }; //变量绑定
    println!("{}", rect.area()); //1500
    println!("{:#?}", &rect); //Rectangle { width: 30, height: 50 }
}

方法签名

  • 在调用方法时,Rust会根据情况自动添加&&mut*(解引用)以便Object匹配方法的签名
struct Point {
    x: f64,
    y: f64,
}
fn distance(p1: &Point, p2: &Point) -> f64 {
    let dx = p2.x - p1.x;
    let dy = p2.y - p1.y;
    let squared_distance = dx * dx + dy * dy;
    squared_distance.sqrt()
}
fn main() {
    let mut point1 = Point { x: 0.0, y: 0.0 };
    let mut point2 = Point { x: 3.0, y: 4.0 };

    let mut dist = distance(&point1, &point2);
    println!("两点间的距离: {}", dist);//两点间的距离: 5
    //等价于
    println!("两点间的距离: {}",&dist);//两点间的距离: 5
    //等价于
    println!("两点间的距离: {}",*&dist);//两点间的距离: 5
    //等价于
    println!("两点间的距离: {}",&mut dist);//两点间的距离: 5
}

关联函数

  • impl块里定义不把self作为第一个参数的函数叫关联函数,如:String::from()
  • 关联函数常用于构造器
  • 每个struct允许拥有多个impl

原文地址:https://blog.csdn.net/weixin_62799021/article/details/140184525

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!