Rust 小记
编程语言是用出来的, rust 文档已经看过几遍,但是依然不得要领. 通过和golang 做类比,以下是一些记录
变量默认不可变,变量可隐藏 .
let 不可变 ,let mut 可变,相比其他语言默认为可变 . 另外就是变了可以通过重新定义的方式.
1 | // 定义一个不可变的变量 |
数据类型
整型、浮点型、布尔类型和字符类是基础类型,字符串切片、结构体 和 golang 大体一致.
1 | // 整型类型 |
元组看使用示例是作为返回场景,golang 的多值返回一致. 其他地方的使用和结构体区别?写法的区别?
元组可以作为函数的返回值或者作为变量的值进行赋值。元组和结构体的区别在于元组的成员没有命名,而结构体的成员是有命名的。
在使用元组时需要注意,如果元组的成员是可变的,那么整个元组也是可变的。如果需要保证元组的成员不可变,可以使用 &
符号将元组的引用作为函数的返回值。
在同组值的时候可以很方便,比如表示坐标 Point(x,y)
看上去元组只时结构体的一种特殊表示.
函数返回值为表达式. 最后一个表达式的值作为函数的返回值.
如何提前返回?
rust 也有 return 关键字, 用于提前返回 ,那最后省略 return 的用意时什么?
在 Rust 中,函数的最后一个表达式的值将自动成为函数的返回值,因此可以省略 return
关键字。
1 | // 使用 `return` 关键字 |
循环
包括了 loop while for ,和 c 语言差不多.相比来说 golang 直接使用 for 来表示循环要简洁一些.
所有权
rust 的每个值都有一个所有权, 那么 = 的意思在其他语言里面的意思是: 把某值给予某变量. rust 里的意思是,把某值的拥有权交给谁.
前者的意思是,把东西放到你的仓库
后者的意思是给你一把仓库的钥匙,而且这个钥匙只有一把(只能有一个所有者,等你离开这里,这把钥匙就销毁,而且仓库也同时清理. (离开作用域,值被丢弃)
- 值传递给函数时,所有权会交给函数.
- println! 宏传递的是引用,不会转移所有权
引用与借用
引用允许在没有所有权的情况下访问变量地址. 有点像 c 语言的指针的概念.
引用能对变量做什么?
引用的变量不可变,所以尝试修改会报错.可以使用 &mut 来定义可变引用 ,这里称之为借用.
借用: 不能在同一时间多次将变量作为可变变量借用. 也就是借用只能同一时间借给一个人,再次使用,只能等前一个还回来.(钥匙只有一把,只能借一个人)
在借用的同时,不能在做引用, 不可变的引用不希望变量被意外改变.
- 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
- 引用必须总是有效的。
枚举
在 golang 里面通常使用常变量的方式来定义一组变量,作为枚举值. rest 不久提供了枚举,还提供了一个通用枚举 Option
从后面的错误处理上看,rust 的枚举并不是单纯的枚举,而是通过枚举来设计了语言特性,比如 match 、错误处理
vector 和 array
vector 和 array 的区别?
vector 的大小不固定,可以动态的扩容. 区别是 vecter 是分配在栈上面.
- 大小可变性:Array 的大小是固定的,一旦定义就不能再改变。而 Vector 的大小是可变的,可以在程序运行时动态地添加或删除元素。
- 分配方式:Array 在栈上分配,而 Vector 在堆上分配。因为 Vector 的大小是可变的,所以需要在堆上分配内存。而 Array 的大小是固定的,所以可以在栈上分配内存,这样可以更快地访问元素。
- 索引访问:Array 的元素可以使用下标直接访问,比如
arr[0]
。而 Vector 的元素也可以使用下标访问,但是需要使用get
方法,比如vec.get(0)
。这是因为 Vector 的大小是可变的,有可能访问不存在的元素,所以需要在访问时进行一些检查。 - 用途:Array 适用于大小固定的情况,比如存储一组固定长度的数据。而 Vector 适用于大小不确定的情况,比如读取文件的内容、网络传输等场景。
1 | // 定义一个数组 |
hashmap
hash map 用法和 golang 基本一样,需要注意一下所有权.
错误处理
rest 使用 Result<T,E> 的枚举来返回是否出现了错误,等同 golang 里面的返回 res,err := function(xxx)
Result<T,E> 定义了一些辅助方法来处理一些情况,比如
1 | let greeting_file = File::open("hello.txt").unwrap(); |
unwrap 成功返回,失败直接 panic
expect 可以自定义错误信息.
可恢复错误: 当出现错误时,不是所有的错误都需要 panic. 所以可以将错误向上传播,使用 ? 来作为向上传播的标识符, ? 实际是一个宏,用来做 match 返回.
1 | let mut f = match f { |
? 给我的感觉是 golang 的 err ≠ nil 的方式相比不够优雅. 不过这都是建立在返回是枚举的基础上.
trait 类似于 golang 的 interface
(rust 的命名如果能和其他语言保持一致的话,可能更加好学一些吧,比如 match 和 switch )
智能指针
智能指针 像是为了解决对应引用问题定义的类型?
从文档来看是来源于 c++ 的概念
Box<T>
,用于在堆上分配值- 解引用 Deref
- 释放资源 Drop
解引用可以将智能指针当普通指针来使用, 通过 deref 将引用返回
释放资源在值离开作用域时调用(手动回收堆上的数据?)
Rc<T>
,一个引用计数类型,其数据可以有多个所有者- 对一个Rc变量进行clone()时,不会将其内部的数据复制,只会增加引用计数。
- 当一个Rc变量离开作用域被drop()时,只会减少引用计数,直到引用计数为零时,才会真正清除其拥有数据的堆内存。
Arc<T>
: Atomic Rc 原子化的Rc<T>
,相比来说,带来线程安全和性能损耗
Ref<T>
和RefMut<T>
,通过RefCell<T>
访问。(RefCell<T>
是一个在运行时而不是在编译时执行借用规则的类型)。Cell
只适用于Copy
类型,用于提供值,而RefCell
用于提供引用Cell
不会panic
,而RefCell
会
并发
rust 提供了线程 、锁,提供了像 golang channel 的类型
async 提供了类似 golang 协程的概念.
总结
这里只是对rust 做了片面的理解, rust 所描述的没有垃圾回收,实际是任何时候,都明确变量在哪个位置被回收
定义好的变量在堆上还是在栈上也是确定的,所以回收的时机也是明确的.
所以不需要垃圾回收算法
rust 的设计上处处都体现着与其他语言的不同,变量、描述、定义,从语法上吸收了 c++ 的语法, 但依然过于复杂. 比如很多约定俗成的描述,依然换了一种描述方式,比如 match 而不是用 switch .
参考: