Nix语言字段递归使用疑问:为何部分场景可行部分不可行?
问题:Nix属性集内变量引用的误区
我在阅读Nix语言教程时看到如下极简包示例:
{ lib, stdenv, fetchurl }: stdenv.mkDerivation rec { pname = "hello"; version = "2.12"; src = fetchurl { url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz"; sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g"; }; meta = with lib; { license = licenses.gpl3Plus; }; }
注意其中fetchurl引用了同一属性集内定义的pname和version,但我自行测试如下代码时:
$ nix-instantiate --eval --strict --expr 'let f = {x}: x+x ; in { a=1; b=2; c=f {x=a;}; }' error: undefined variable 'a' at «string»:1:42: 1| let f = {x}: x+x ; in { a=1; b=2; c=f {x=a;}; } | ^
出现了未定义变量的错误,想请教我存在哪些理解误区?
核心原因:rec关键字的作用
- 示例代码里用了
rec { ... }语法,这个关键字的作用是让属性集内的属性可以互相引用——在rec包裹的属性集中,每个属性定义时都能访问同一属性集里的其他属性(包括在它之前定义的)。 - 你自己的代码里,
{ a=1; b=2; c=f {x=a;}; }是普通属性集,没有rec修饰。普通属性集里的属性定义是上下文独立的,c的定义里的a会被当作外部变量查找,而不是当前属性集里的a,所以会报错“未定义变量”。
修正后的代码示例
如果要让你的代码能引用同一属性集内的a,只需要给外层属性集加上rec:
$ nix-instantiate --eval --strict --expr 'let f = {x}: x+x ; in rec { a=1; b=2; c=f {x=a;}; }'
执行后会返回正确结果:{ a = 1; b = 2; c = 2; }
额外说明
rec是Nix里专门用于创建递归属性集的语法,常见于mkDerivation的参数中,因为包定义里经常需要用pname、version拼接下载链接、输出路径等。- 注意不要滥用
rec,如果属性之间出现循环引用(比如a = b; b = a;),会导致求值无限递归,触发错误。
内容的提问来源于Stack Exchange,提问作者Jan
相关产品推荐
相关产品推荐

