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不可用
integer
、array
等大小固定的值存储在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:u8、i32
bool
char
float:f32、f64
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)!