OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 023

Nix 属性集:递归与合并

区分语言层浅合并和 NixOS 模块选项的合并规则。

所在专题Nix 语言与构建模型 ↗
约 3 分钟阅读开始阅读

"Nix 配置的精髓:如何在复杂的层级中找到正确的合并点。"


递归属性集:rec { ... }#

普通属性集不会把自身属性加入词法作用域。要按属性名相互引用,可以使用 rec,也可以先在 let 中定义共同绑定;外层有同名变量时普通属性集引用的是外层变量。

错误:

{
  a = 1;
  b = a + 1; # 报错:变量 a 找不到
}

正确:

rec {
  a = 1;
  b = a + 1; # 结果为 b = 2
}

属性合并的真相#

1. 使用 // (Merge)

这是 Nix 语言层面的合并。

let
  defaults = { user = "guest"; shell = "/bin/sh"; };
  overrides = { shell = "/bin/bash"; };
in defaults // overrides
# 结果:{ user = "guest"; shell = "/bin/bash"; }
  • 注意:// 是浅合并。如果两个 Set 里都有同名的嵌套 Set,后者会直接覆盖前者。

2. 使用 lib.mkMerge (NixOS 模块专用)

在 NixOS 配置文件中,我们很少直接用 //。NixOS 模块系统会自动帮你合并配置:

{
  environment.systemPackages = [ pkgs.git ];
}
# 另一个模块中:
{
  environment.systemPackages = [ pkgs.vim ];
}
# 结果:[ pkgs.git pkgs.vim ] (列表选项默认按模块规则合并;顺序可用 mkBefore/mkAfter 调整)

点符号路径 (Dotted Path)#

为了减少 nesting,可以使用点符号直接定义深层属性。

长写法:

{
  services = {
    openssh = {
      enable = true;
    };
  };
}

简洁写法:

{
  services.openssh.enable = true;
}

实战案例:动态属性名#

通过 ${...} 可以动态生成属性名。

let
  username = "alice";
in {
  users.users."${username}" = {
    isNormalUser = true;
  };
}

参考与延伸

  1. NixOS 手册 ↗
  2. Nix 语言手册 ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

Nix 语言与构建模型

  1. 01Nix 与 NixOS:存储、回滚和声明式思维
  2. 02Nix 语法:属性集、列表与内置函数
  3. 03Nix 函数与作用域
  4. 04Nix 属性集:递归与合并
  5. 05Derivation:从表达式到构建产物
CONTINUE READING / 下一篇Derivation:从表达式到构建产物

追踪求值、构建或替代下载以及 store 输出之间的关系。