二-内存模型及所有权和引用、借用

1. 内存模型1

内存模型,heap和stack的区别,GC方面和go的区别

基本同go一样,分为堆内存、栈内存。栈内存函数退出时会自动释放,大小有限,一般是比较"小"的变量存到栈上。

比较"大"的或者大小动态变化的会分配到堆上。同时为了使用这个值,需要在栈上有一个变量指向这个堆的地址。

和go的区别在于,go是GC系统在运行时动态记录堆内存的使用情况,自己来清理,用户完全不用管。c这类完全交给用户处理,总是容易出错。

Rust介于两者之间,通过所有权和生命周期的机制,在编译阶段识别到堆内存的使用情况,等到不再被使用时,会自动清理。

2. String类型

整个高级点的东西,继续说明上面的内存问题,基于这种类型讲所有权、借用才有意义

这里的String类似go里面的字符切片,var stringSlice []byte,结构也类似,有一个结构体 包含指针、cap、len 三字表头,然后指针指向堆内存的一个 "底层数组"。

根据结构可知,既然涉及到堆内存,就关系到内存释放问题,对于Rust就是通过所有权和生命周期机制,"自动"释放内存。

使用示例如下:

rust 复制代码
// 注意 mut 声明的才可以继续进行修改操作
let mut string1 = String::from("String hello");
string1.push_str(", world");
println!("{}", string1);

3. 所有权,引用、可变引用、借用,

3.1 先看一个示例

rust 复制代码
fn main() {
    let mut s = String::from("Hello ");
    s.push_str("world");
    // 把s做为参数传给另一个函数,这个在go里很常见,会拷贝s的皮,但是用一套底层数组
    just_print(s); //  - value moved here
    println!("{}", s);
}

fn just_print(s: String) {
    // do some thing
    println!("{}", s);
}

这时学go同学惊呆了,后面s竟然是无效的了,感觉很不合理,明明有这个变量,竟然会无效;学c++的同学,可能联想到,如果这个指针,在最后一行打印之前,被Delete了,那的确会异常。

所以有GC语言的同学,如果接受不了,存在变量,但是无效的情况,可以结合C++想一下,这不就是写了个bug。

3.2 所有权转移

上文示例,其实就是所有权转移,Rust通过一个堆内的"对象",只能有一个变量拥有它的所有权这种方式,就能很好的追踪何时可以自动GC,在编译的时候,追踪这个对象的所有权变量,以及这个变量的使用情况,就可以在不用的时候释放内存。

rust 复制代码
let string1 = String::from("a");
let string3 = string1; // 这里会发生所有权转移,此时string1就无效了 println!("{}", string1);报错。

这样的限制,避免一个堆内存被多个变量指向,一是写代码的人容易勿删导致悬垂引用,二是只有一份所有权的情况下,编译器就可以盯着有所有权的这个变量的生命周期的范围,从而知道啥时候到期进行自动GC

3.3 mut

先说说这个,这个其实比所有权 应用什么的简单很多,就是控制能不能修改而已。

通过示例可以知道,是不是可变的关键在于用let绑定或者传参的时候,有没有mut,和之前的那个变量是不是可变的无关。

rust 复制代码
// string3.push_str("afs"); // 这时不能
let mut string4 = string3;
string4.push_str("!!! change to mut.");
println!("{}", string4);

3.4 引用、可变引用、借用

所有权每次都要来回转移,只能转移到一个上面,很是麻烦,尤其是函数传参的情况,作为参数传进去之后,如果不再返回回来,编译器就认为这个堆内存的生命就到这了,就给GC了。

所以增加了引用的概念,让事情简单一些,也叫借用borrow,之前所有权转移叫 move。

rust 复制代码
let string4 = get_then_return(string4);// 这里甚至有整成了不可变的,所以mut相对随意,甚至如果所有的都加mut就和其他语言一样了,但是引用那里有限制。
println!("{}", string4);
let s = &string4;

可变引用又会导致数据竞争问题,

3.5 引用借用的关系

这两个词经常一起出现,后面看了Rust程序设计第二版才清楚。引用是对&这种方式的表述,借用则是表示了他真正的道理:所有借用,必须归还,即借用不会影响原有的生命周期,反而受原有变量的生命周期控制。

引用是一个用于指代另一个值的值,需要里面的数据时,可以解引用。这个在go语言中不存在,因为go只有值传递,没有引用传递。

rust 复制代码
fn main() {
    let a = 10;
    let ai = &a;
    assert_eq!(*ai, a);
}
相关推荐
-凌凌漆-14 分钟前
【Qt】QStringLiteral 介绍
开发语言·qt
程序员爱钓鱼14 分钟前
Go语言项目工程化 — 常见开发工具与 CI/CD 支持
开发语言·后端·golang·gin
军训猫猫头1 小时前
1.如何对多个控件进行高效的绑定 C#例子 WPF例子
开发语言·算法·c#·.net
真的想上岸啊1 小时前
学习C++、QT---18(C++ 记事本项目的stylesheet)
开发语言·c++·学习
明天好,会的1 小时前
跨平台ZeroMQ:在Rust中使用zmq库的完整指南
开发语言·后端·rust
丁劲犇2 小时前
用 Turbo Vision 2 为 Qt 6 控制台应用创建 TUI 字符 MainFrame
开发语言·c++·qt·tui·字符界面·curse
旷世奇才李先生2 小时前
Next.js 安装使用教程
开发语言·javascript·ecmascript
charlie1145141913 小时前
深入理解Qt的SetWindowsFlags函数
开发语言·c++·qt·原理分析
likeGhee3 小时前
python缓存装饰器实现方案
开发语言·python·缓存
whoarethenext3 小时前
使用 C++/Faiss 加速海量 MFCC 特征的相似性搜索
开发语言·c++·faiss