Rust 语言学习笔记(一)

学了不少编程语言,多数是离不开垃圾回收的,要么像 C++ 仍然是过于复杂,对于通用,编译型编程语言 Rust 是个不错的选择, Rust 不需要垃圾回收器。Rust 是由 Mozilla 主导开发的,设计准则为 "安全,并发,实用", 支持函数式,并发式,过程式以及面向对象的编程风格。Rust 能达到与 C++ 接近的性能,它又是编译型语言,编译出来二进制文件执行时不再依赖于运行时。Rust 有自带的 Cargo 作为依赖管理与构建工具,免除了关键工具的选择综合症。AWS 在今年也推出了 Rust 的 AWS SDK, 所以学习 Rust 的过程中也打算使用它来操作 AWS 的资源。

Rust 的 Hello World

在 macOS 下的安装

$ brew install rust

$ curl https://sh.rustup.rs | sh    # 安装
$ rustc --version                              # 查看版本
$ rustup update                               # 更新
$ rustup self uninstall                     # 卸载

当前 Rust 版本为 1.74.0, 安装后有 rustc, rustdoc, rust-gdb, rust-lldb, cargo 等相关命令

创建一个并运行 hello

cargo new 生成的项目有一个标准布局,不像 C++ 的项目那么自由混乱。自然的,Cargo.toml 中可配置项目的信息及管理依赖。

src/main.rs 的内容如下

main.rs 中的 Hello World 代码本身没多大意义,我们由此可了解 Cargo 的使用

cargo run 生成的二进制文件是在 target/debug/hello,可以直接执行它或分发到相同的平台中,不需要预先安装任何的运行时。

Cargo 也像 Maven 那样,用 cargo --help 可查看所有的构建命令。cargo xxx -v 可看到具体调用的对应命令的详细命令。

cargo doc 会生成自己项目和所用到的所有第三方依赖的文档,用 cargo doc --open 能一步生成文档并在在浏览器中打开相应的 index.html 文件。如果只需生成自己代码的文档用 cargo doc --no-deps

Rust 的注释格式:

  1. // 普通单行注释
  2. /* */ 块注释
  3. //! 当前项目的标识注释
  4. /// 文档注释,可应用于处,像 Java 的 /** **/, 其中支持 Markdown

比如我们可以重新构前面的 hello 执行文件

$ cargo clean
$ cargo build --release
$ target/release/hello
Hello, world!
$ ls -l target/release/hello
-rwxr-xr-x 1 yanbin staff 488704 Dec 30 23:40 target/release/hello

执行文件 488K (通过 strip 可缩减到  363K)

在 Ubuntu 20.04 中可以用 apt 命令安装 rust

sudo apt install -y rust-all

然后在 Ubuntu 20.04 下编译 Rust Hello World

$ cargo new hello && cd hello
$ cargo build --release
$ ls -lh target/release/hello
-rwxrwxr-x 2 vagrant vagrant 13M Dec 31 06:26 target/release/hello
$ strip target/release/hello
$ ls -lh target/release/hello
-rwxrwxr-x 2 vagrant vagrant 327K Dec 31 06:26 target/release/hello
$ cargo rustc --release -- -C prefer-dynamic
$ ls -lh target/release/hello
-rwxrwxr-x 2 vagrant vagrant 17K Dec 31 06:27 target/release/hello

直接构建生成的可执行文件 13M, strip 之后是 327K, 只要没有使用 -C prefer-dynamic 构建的执行文件挪到其他没安装 rust 的 Linux 下可直接执行(当然要匹配 CPU 架构了)。如果用了 -C prefer-dynamic 生成的只有 17K, 它运行时还须连接到 Rust 运行库,所以拿到一个纯净的 Ubuntu 20.04 下直接执行的话,出现如下错误

$ ./hello
$ ./hello: error while loading shared libraries: libstd-90f6ddbf82de36ec.so: cannot open shared object file: No such file or directory

如果把安装了 rust-all 机器下的 /usr/local/rustup/toolchains/1.75.0-x86_64-unknown-linux-gnu/lib/libstd-90f6ddbf82de36ec.so 文件拷到那台纯净 Ubuntu 20.04 的当前目录下,并且设置 export LD_LIBRARY_PATH=., 再执行 ./hello 就没问题了。libstd-90f6ddbf82de36ec.so 文件大小有 14M。

strip 和 -C prefer-dynamic 可配置在 Cargo.toml 文件中

注:如果用 docker 镜像 rust:1.75-buster 的 cargo build --release 生成的 target/release/hello 文件大小是 4.3M, strip 之后是  355K

在 macOS 下可通过 Docker 来编译出 Linux 版本的二进制执行文件。IntelliJ IDEA 中可用 Dev Containers 插件,然后当前目录中创建 .devcontainer/devcontainer.json 文件,基本内容

在 devcontainer.json 上下文菜单中有 Dev Containers 菜单来启动开发容器. devcontainer.json 的详细配置请参考 Dev Container metadata reference.

使用 IntelliJ IDEA 的话,在运行 Cargo run 时可指琮 Run on -- SSH 或 Docker

Rust 支持跨平台编译, 比如在 macOS 下编译出 Linux 下的可执行文件,这需另开一个专题来研究。

如果简单的代码不使用 Cargo 的话,可直接创建 main.rs 源文件,然后用

$ rustc main.rs

就会生成可执行的 main 文件

Rust 语言基本采用了 C/C++ 的大括号/分号的代码风格。最后来一段代码作为对 Rust 的总体的感受

因为是编译型语言,Rust 的函数不需像 Python 那样提前声明,但作为编译型的 C 也是要提前声明函数的。

Rust 函数默认的返回值是 (), 读作  unit, 和 Scala 的空返回值是一样的。 声明函数的返回值用 ->, 如 fn foo() -> String {...}。Rust 的赋值语的返回值不是最终的变量值,而是 (), 所以即使变量 a 是 bool 类型,if a = true {} 也是非法的,更不需要像 Java 中建议的 if true == a 的写法。

Rust 像 C/C++/Objective C 那样也是支持宏的,Rust 的宏支持可没有 Scala 的那么复杂

Rust 的编译器尽可能的做更多的事情,更多的优化,以使用在程序运行时更安全,更好的性能。

像局部变量因为是在内部使用,它会建议声明为下划线开始的变量名,如 config -> _config。未使用的变量会有警告,但不像 Go 编译器发现未使用的变量直接报错 ; 未使用的 use 引用也会有警告。

比如线程竞争访问外部变量,访问删除(drop) 的变量,像上面的数值溢出,遍历集合时集合被修改了编译时都会有警告。在编译信息中经常看到 borrow, move, trait 这样的表述。

Rust 方法默认是静态分派的,和 C++ 一样,方法默认不是虚的; 而 Java 默认都是虚方法,这也会降低程序执行的性能。

链接:

  1. Cross-compiling Rust From Mac to Linux

本文链接 https://yanbin.blog/rust-language-learning-1/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments