最近对 Rust 编程语言有了兴趣,特别是其标榜的无 GC 和内存安全特性。Rust 官网提供了一个 Playground,可以零成本的在线尝试这门语言。通过这个工具搭配官方教程掌握了一点基础语法后,我决定在本地搭建一个开发环境来深入了解一下 Rust。
我使用 macOS 系统,按照官网安装说明,只需要在终端执行下面这条命令即可完成 Rust 的安装:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
不过鉴于我目前的开发环境都是基于 Docker,所以我还是准备用 Docker 来搭建一个本地 Rust 开发环境,这样如果不想学了清理起来也简单,对系统文件的干扰也最小。
首先创建一个给 Rust 工具使用的服务目录:
mkdir -p ~/Service/rust
然后在这个目录下创建 Dockerfile,内容如下:
# 拉取最新的 rust 镜像
FROM rust:latest
# 设置 rustup 命令的国内镜像变量
ENV RUSTUP_UPDATE_ROOT="https://mirrors.ustc.edu.cn/rust-static/rustup" \
RUSTUP_DIST_SERVER="https://mirrors.ustc.edu.cn/rust-static"
# 添加自定义镜像构建脚本
ADD build.sh /tmp/build.sh
# 执行镜像构建脚本
RUN chmod +x /tmp/build.sh && /tmp/build.sh
然后创建构建镜像的自定义脚本: build.sh,代码如下:
#!/bin/sh
set -x
# 创建 Cargo 软件包目录
mkdir -vp ${CARGO_HOME:-$HOME/.cargo}
# 设置 cargo 软件包的国内镜像
cat << EOF | tee -a ${CARGO_HOME:-$HOME/.cargo}/config.toml
[source.crates-io]
replace-with = 'ustc'
[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
[registries.ustc]
index = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
EOF
# 镜像内系统国内软件源配置
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
# 安装依赖
apt-get update
apt-get install gcc-x86-64-linux-gnu -y
# 安装交叉编译目标所需对象
rustup target add x86_64-unknown-linux-musl
# 安装编译热更新工具
cargo install cargo-watch
# 清理镜像打包缓存和临时文件
apt-get autoclean
rm -rf /var/lib/apt/lists/*
rm -rf /tmp/*
上面的自定义镜像构建脚本代码有点多,都是根据我需要添加,主要涵盖三个目的:
- Rust 使用
cargo
命令安装第三方包,默认从国外下载速度较慢,所以需要使用国内的镜像,上面使用了中科大提供的镜像。 - Rust 作为编译语言,最终生成的是一个可执行文件,这个文件跟编译时的系统有关。比如我默认拉取的是 ARM 版本的系统,最终编译出来的可执行文件就只能在 ARM 环境运行。如果想在 x86 环境运行,就需要用到交叉编译,所以我通过
rustup
命令添加了 x86 的编译目标。 - Rust 作为编译语言,每次修改代码后都需要重新编译才能看到变更效果,用惯了动态语言的我对这点非常不习惯,所以通过
cargo
命令安装了 cargo-watch 这个神器,后续通过它可以实现修改后自动重新编译。
完成以上准备后,使用 docker
命令打包镜像:
docker build \
--no-cache --progress plain \
-t zzxworld/rust:dev .
至此我的 Rust 开发环境基本准备妥当,通过 docker
命令已经可以体验在本地编译运行 Rust 代码的乐趣:
docker run --rm -it \
-v ${PWD}:/srv/app \
-w /srv/app \
zzxworld/rust:dev cargo run
但每次都要输入这么长的 Docker 命令也太麻烦了,所以我创建了一个自定义 cargo
命令:
#!/bin/bash
# 创建 cargo 命令本地软件库目录
CARGO_REGISTRY_PATH=$HOME/Services/rust/cargo/registry
if [[ ! -d $CARGO_REGISTRY_PATH ]]; then
mkdir -p $CARGO_REGISTRY_PATH
fi
# 镜像名称
DOCKER_IMAGE="zzxworld/rust:dev"
# 容器运行参数
DOCKER_ARGS=" \
-u $UID:$UID \
-v ${PWD}:/srv/app \
-w /srv/app \
-v ${CARGO_REGISTRY_PATH}:/usr/local/cargo/registry"
if [[ $1 == 'stop' ]]; then
# 停止运行的 rust 容器
docker ps | grep 'rust-cargo' | awk '{print $1}' | xargs docker stop
elif [[ $1 == 'build' && $2 == '--to-x86' ]]; then
# 交叉编译到 x86 环境的命令
docker run --rm -it ${DOCKER_ARGS} \
-e RUSTFLAGS="-C linker=x86_64-linux-gnu-gcc" \
-e CC_x86_64_unknown_linux_musl="x86_64-linux-gnu-gcc" \
${DOCKER_IMAGE} cargo build -r --target=x86_64-unknown-linux-musl
else
# 执行 cargo 命令
docker run --rm -it ${DOCKER_ARGS} \
-p 8000:8000 \
--name rust-cargo \
${DOCKER_IMAGE} cargo $@
fi
把这个文件命令为 cargo.sh,然后添加可执行权限,再到系统环境变量配置文件中映射为 cargo
命令:
alias cargo='~/Services/rust/bin/cargo.sh'
这样我就可以在任何地方直接使用 cargo
命令,和官方安装后的效果一致,比如创建一个新项目:
cargo new zzx-blog
或是直接运行项目:
cargo run
当然,我还是觉得通过 cargo watch
检测文件修改后自动编译的体验更好:
cargo watch -x run
除了以上正常的 cargo
命令,上面脚本中我还添加了两个扩展命令,一个是:
cargo stop
添加这个命令的原因是容器中运行的 cargo
命令无法通过 Ctrl + c 快捷键终止,只能通过停止容器的方式来结束容器内程序进程。
另外一个是:
cargo build --to-x86
扩展这个命令是为了方便执行交叉编译命令,不然老是要输入一大堆交叉编译参数也是很难受的。Rust 还有一些其他工具命令,不过目前我都还用不到,所以先就这样吧。