Nix创建虚拟环境是否始终具备确定性?Rust版本留存疑问
问题分析与解答
你的flake配置
{ inputs = { nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable; flake-utils.url = github:numtide/flake-utils; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; in { devShells.default = with pkgs; mkShell { packages = [ cargo rustup rustc rust-analyzer rustfmt cmake ]; RUST_SRC_PATH = rustPlatform.rustLibSrc; }; } ); }
操作流程
你通过以下命令进入隔离环境:
nix develop -i
其中-i(--ignore-environment)会清除大部分系统环境变量,仅保留flake定义的依赖,但仍会保留HOME环境变量。
在环境内执行:
rustup toolchain list > 1.70 //正常 rustup toolchain install 1.69 rustup default 1.69 rustc --version > rustc 1.69.0 (84c898d65 2023-04-16) //正常
退出环境后重新进入,发现:
rustc --version > 1.69
原因说明
这不是Nix的bug,是正常机制:
- Rustup的配置文件、已安装的toolchain默认存储在用户主目录的
~/.rustup和~/.cargo目录下,这些属于用户持久化数据,不在Nix的隔离范围内。 nix develop -i虽然隔离了系统的PATH和其他环境变量,但不会修改或隔离你的HOME目录,因此rustup的设置会被持久保留。- Nix的确定性体现在flake定义的依赖包本身是固定的(比如你flake里的rustc、rustup都是nixpkgs-unstable里的特定版本),但rustup作为用户态工具,它的操作会写入用户HOME,这部分是用户可控的,不属于Nix的包管理范畴。
解决方案
如果想要完全确定性的Rust环境,不需要用rustup,直接在flake里指定所需的rust版本:
# 修改flake里的devShells部分 devShells.default = with pkgs; mkShell { # 直接使用指定版本的rust工具链,比如rust_1_69 packages = [ pkgs.rust_1_69.cargo pkgs.rust_1_69.rustc rust-analyzer rustfmt cmake ]; RUST_SRC_PATH = pkgs.rust_1_69.rustLibSrc; };
这样每次进入环境都是固定的1.69版本,不会受用户HOME里的rustup配置影响。
或者,如果你一定要用rustup且希望隔离环境,可以使用nix develop --pure(比-i更严格的隔离)配合临时HOME目录,但这样每次进入环境都需要重新安装toolchain,适合一次性测试场景:
HOME=$(mktemp -d) nix develop --pure
内容的提问来源于stack exchange,提问作者Pierre-olivier Gendraud
相关产品推荐
相关产品推荐

