背景
我呢是个半路出家的前端,之前是做设计的,属于地产这个大板块。 当年眼看着地产行业日薄西山,同时自己躁动着的想学编程心越发难以遏制。 干脆辞职报了个前端培训班。当然选择学前端也是经过“深思熟虑”的。
- 一方面,设计这个东西离前端还算近,毕竟很多设计都转 UI 了嘛。
- 另一方面,前端相比后端也更好入门,更容易上车。
现在想来,emmmm,果然人类一思考上帝就发笑。当年转前端后,地产相关的设计公司很快就工资都发不出来啦。心中还是有一些劫后余生的小窃喜,以及及时止损并转头上新车的小骄傲的。
但是现实很快就给了我一记响亮的耳光。这不今年在降本增效的浪潮下,被裁了。 同时呢,各种前端药丸的言论不绝于耳。 有种把自己干成了行业冥灯的感觉。
找了好几个月工作,先后去了两家外包,目前在第二家外包。 找工作呢已经变得佛系了,随缘吧,机缘巧合,王八看绿豆能看对眼就去,不行呢就在外包先干着。
现在前端市场变化很大,已经是一个供大于求的状态了,再也不可能像当年从培训班出来的时候那么好找了。 于是面试的问题也就越来越刁钻。 自己半路出家基础不牢的短板就暴露的很彻底。 不过好歹在外包还有钱赚,生活还得以为继。
人嘛,总是会产生路径依赖。 我的习惯呢,就是在职业低谷期搞点新东西,让自己的技能树xue微枝繁叶茂一点点。 就像从设计转前端时一样。也和考研时候从农业相关转到设计相关一样。 我管这叫做路线修正。哈哈哈哈。
不过这次倒不是打算脱离开发这个大的框架,因为对我而言,开发确实是个还不错的行业。 简单的来说,我打算在低谷期学点啥。
其实,在这期间,断断续续的看过一些计算机组成原理,数据结构与算法以及网络相关的内容。 这些学习当然让我这个半路出家的学到了很多。 但是,没有使用场景的学习,很容易学完就忘。
于是,我想我应该掌握一门除了 JavaScript 之外的语言,同时用新的语言去做一些项目,以期使自己的编程能力得到比较大的提升。 在选择新语言的时候,Python、Java、GO、C 都在我的考虑之列。 最终被 Rust 所吸引。
好多号称比 XXX 提升 XXX 倍性能的前端工具都是 rust 写的。听着就让人心血来潮。 让人感觉光怪陆离的 web3 世界,rust 也是数一数二的存在。 同时,它还连续 8 年被选为最受欢迎语言。再同时,学习 rust 的路线很陡峭,需要程序员对底层有相当的了解。等等等吧。
它太适合当下的要选择第二门语言来妄图提升自己的我了。
学习资料
目前主要通过以下渠道来学习:
- 圣经:course.rs/about-book.…
- Rust 程序设计语言 简体中文版:kaisery.github.io/trpl-zh-cn/…
- Rust编程语言入门教程(Rust语言/Rust权威指南配套):www.bilibili.com/video/BV1hp…
- Rust 语言从入门到实战;
前两个是文档,最后一个是视频。 我自己感觉还是看视频爽,这个老师讲课一点不拖泥带水,很棒棒。视频有助于快速形成概念,文档适合反复查阅,重难点突破。 如果谁有更好的学习资源,分享给我!!!磕头。。。
所有权
现在已经学到所有权啦。数据类型、流程控制这些有编程基础的情况下很容易理解。 忘了说了,这文章我是打算当作自己的笔记来着。如果能对想学习 rust 的同学起到哪怕一点点作用。那真是善莫大焉。
为什么要有所有权
所有权是什么,为什么要引入所有权? 所有权是 rust 的内存管理方案。 目前市面上主要有三种内存管理方案。
- GC 垃圾回收:Java、JavaScript、Go 等
- 无 GC,手动回收:C、C++
- 所有权:Rust
GC 解放了程序员,无需关注内存释放,程序运行时通过运行垃圾回收来检查未被回收的内存,并将其回收归还给操作系统。同时也带来了运行时开销。
手动回收则是完全信任程序员,需要程序员正确的跟踪每次内存使用,并在使用完后归还给操作系统,每次使用只能回收一次,不回收或者多次回收都会引发安全问题。 对程序员来说无疑增加了更多的心智负担。
Rust 走的是第三条路,即所有权。通过所有权机制,rust 编译器就做了垃圾回收,既避免了 GC 带来的额外开销,又对内存进行了跟踪和回收,将垃圾回收提前到编译时。
stack VS. heap
要讲清楚所有权,就要先明白栈内存和堆内存。 栈内存和堆内存是两种不同的内存组织方式。
栈内存
栈内存是栈结构的,后进先出,或者说先进后出。在栈中的数据大小是确定的。 程序执行时候会反复进行压栈、出栈。
堆内存
堆内存中的数据大小是不确定。
数据存储到栈内存上比存储在堆内存更高效,因为每次的存储地点都是栈顶。而堆内存每次需要系统分配足够的空间,并将相应的内存标记为已使用,同时返回该内存的指针。指针的大小是确定的,因此可以在栈内存中使用。
所有权以及垃圾回收就是针对堆内存的。
所有权要解决的问题
- 跟踪代码的那些部分正在使用 Heap 上的哪些数据
- 最小化 Heap 上的重复数据量
- 清理 Heap 上未使用的数据以避免空间问题
所有权规则
每个值有且只有一个对应的变量,该变量称为该值的所有者,在值的变量(所有者)离开作用域(scope)后,该值被删除,值所使用的内存立即交还给操作系统(rust 调用 drop 函数)。
存放在堆上的值的变量包含三部分:
- ptr:指针
- len:数据所占用的内存长度
- capacity:申请到的内存容量
移动(move)
或者说转移,浅拷贝+之前的变量失效:
fn main(){
let a = new String::from("hello");
let b = a;
println!("{}",a)
}
对于上面的代码,a 所指向的变量是 String 类型,存储在堆内存,所有权系统对其起作用,let b = a;的时候,发生 move,即值的所有权转移到 b,那么变量 a 就失效了,后面再引用 a 就会报错。
为什么要这么设计呢?a 不失效可以吗?如果 a 不失效,那么当 main 函数执行结束的时候,a 和 b 都离开了作用域,rust 会调用 drop 函数回收他们对应的堆内存,这样就会回收两次,会导致野指针等问题。
其实 a 也是可以不失效的,那就要用到 clone 函数了,clone 会完成深拷贝,即拷贝变量的ptr、len、capacity 信息的同时,将堆内存的数据也进行拷贝:
fn main(){
let a = String::from("hello");
let b = a.clone();
println("{}",a);
}
由于用到了 clone 函数,上面的代码是不会报错的,println中引用 a 是可以的。
copy
上面讲到了,所有权系统主要针对的是堆内存上的数据,那么栈上的数据做相同的操作会如何呢?
fn main(){
let a = 5;
let b = a;
println("{}",a);
}
答案是,不会报错,可以打印 a 的值。对于简单类型,或者说标量类型的值,直接进行复制开销也是很小的,因为它们的大小固定且不大。 语言层面,实现了 copy 这个 trait 的类型都会有以上效果,即旧变量不会失效。
上面还提到了 drop 函数,实现了 drop 函数的类型,rust 不允许它实现 copy trait。也很好理解,copy 对应存放在栈内存的数据,drop 对应存放在堆内存的数据。
trait暂时理解为接口。
实现了copy trait的类型
-
简单标量的组合类型
-
任何需要分配内存(堆内存)或某种资源的都是无 copy 的
-
拥有 copy trait 的类型:
- 整数
- bool
- char
- 浮点类型
- 所有字段可 copy 的元祖(tuple)
结尾
累了,码字也挺累人,今天的笔记就先到这。如果有错误的地方,希望大家多多指正。如果有好的学习资源或者学习方法,也请多多指教。我去看视频了。。。