对rust语言的一些理解

近来在研究rust语言,作为老牌的C++程序员及拥有近10年经验的java程序员,觉得有必要通过语言间的对比来加深对rust的理解。

环境

安装

rust安装是区分操作系统和ABI的,比如我的是windows+gnu ABI,主要是懒得安装VC

几个重要工具

rustc类似于 gcc

rustup:rust工具链管理,工具链包括:rustc编译器、标准库、cargo等,工具链也是区分平台和底层ABI的。

cargo类似于java的maven,toml类似于maven的pom,crate就是java里的artifact。cargo里的registry类似于maven远程仓,只不过registry是复用git组织的,其中的crate都是通过git下载。

内源crate仓

如要使用非官方crate仓,cargo的config文件可以这样配置:

[source.crates-io]
replace-with = 'mirror'
[source.mirror]
registry ="https://XXX.com/rust/crates.io-index/"

假设我们在toml文件里增加了libc依赖:

[dependencies]
libc = "0.2.153"

键入cargo build命令,cargo会自动为我们下载三方依赖。下载的三方依赖会放到.cargo/registry下的cache和src目录,前者是二进制包(后缀为.crate),后者是三方件源码。

rust标准库

rust标准库则使用rustup安装,下载路径为(我的环境使用了windows gnu ABI):

.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\src\rust\library

IDE

可以下载jetBrain的RustRover,因为使用非官方仓,所以要记得把RustRover里的http proxy禁用,不然crate可能下不下来。

语言

推荐rust入门中文教程链接:

https://kaisery.github.io/trpl-zh-cn/ch02-00-guessing-game-tutorial.html
除了介绍语法,还会讲一些语言设计上的考虑,值得细读。

个人理解

let默认定义的是不变量,而非其它语言那样是个可变量。

专门用mut关键字表示可变量(mutable)。之所以提出这个关键字,是因为并发编程下,const是万善之首,mutable是万恶之源,rust编译器要特别关注mut的情况,并对其使用作出一定的限制。

rust没有异常,使用Result枚举+match来处理结果。本质上是C的错误码那一套。C的那一套,我个人是不太喜欢的,这种约束意味着程序员要花费过多精力去关注错误,代码整体可读性会比较差。不过rust针对这种情况,提供了?运算符,可以极大简化错误处理的套路代码,算是对代码可读性的一个补偿。

rust里的对象,用引用传递(rust术语叫borrow,借用)没问题,用值传递会导致所有权转移,本质上类似于C++里的auto_ptr。相当于把C++里标准库的能力下沉到语言层面,这样编译器就能发挥提前示警的作用(比如给出value move错误),在C++里,因为auto_ptr只是库的能力,C++编译器对value move的风险自然无从感知。另外,对象的传递,要么引用、要么所有权转移,尽量减少C++那样的拷贝构造开销(即value copy),效率上也会更高。

具体到String这个类,从功能的角度看,rust里的String等价于java的StringBuilder, &str才等价于java的String(java里String是不可变的);从资源管理的角度看, rust的String类似于C++里的std::auto_ptr<std::string>(std::string底层实现也是一个堆上的字符数组),&str接近于const std::string&。所以rust的引用操作符作用相当于C++的*(std::auto_ptr.get())。

rust里的drop函数等价于C++的析构函数(destructor),能做到超出范围的对象自动释放内存(资源分配即初始化,RAII)。表面上就像有gc一样。一开始我还奇怪rust应用层里怎么没有内存释放的代码呢。

rust语言的设计原则,一言以蔽之:预防性编程,宁杀错不放过。比如有些immutable borrow+mutable borrow+immutable borrow的场景,其实执行的时候不一定会出错(因为mutable borrow也可能只用来做只读操作,并不真的修改对象,见下例),但rustc为了避免后续可能的错误,还是会让你的代码编译失败。如此严格的编译规则,你想犯错都不容易。

fn dangle() -> String {
    let mut s = String::from("hello");

    let s1 = &s;  // immutable borrow occurs here
    let s2 = &s;
    let s3 = &mut s;  // mutable borrow occurs here, error!
    println!("{}", s1); // immutable borrow later used here
    println!("{}", s3);
    s
}

slice是对一段连续内存中部分区域的引用,它的出现,一方面是编程的易用性、安全性考虑,一方面也是为了性能。设想一个常见场景:对一个字符串按特定符号分割出一个列表,然后对这个列表做遍历。在java里就只能用substring方法复制出一组小的字符串来。因为我们遍历是只读的,这个复制小字符串的行为其实很浪费内存,在C或C++中完全可以在char*数组中用下标前移的做法来遍历,从而节省额外拷贝的开销,提高效率。rust的slice就是上述两者的一个折中,既保证了java那样的易用性、封装性,又兼顾了C和C++做法的效率。是一个非常好的设计。从slice的设计上,可以猜测,rust性能号称对标C,当非虚妄。

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值