第 6 章 · 模块、错误与标准库:脚本的边界 章节摘要:脚本的行李箱里装着三样东西:模块(代码怎么拼装成可管理的单位)、错误处理(出错时谁兜底)、标准库(自带的家当有多少、哪些能被没收)。本章三节分别打开它们:6.1 讲 require 的加载协议与模块编写范式,6.2 讲 error、assert、pcall 的防护哲学与调试手段,6.3 巡礼标准库并画出沙箱边界——宿主如何把危险能力挡在门外。这三节合起来回答"脚本的边界在哪、边界两侧怎么协作"。 学习目标 阅读完本章,你应当能够: 说清 require 的完整加载流程(查缓存、找加载器、执行、登记),理解 package.loaded、package.path 的作用,手写规范的模块;
章节摘要:脚本的行李箱里装着三样东西:模块(代码怎么拼装成可管理的单位)、错误处理(出错时谁兜底)、标准库(自带的家当有多少、哪些能被没收)。本章三节分别打开它们:6.1 讲 require 的加载协议与模块编写范式,6.2 讲 error、assert、pcall 的防护哲学与调试手段,6.3 巡礼标准库并画出沙箱边界——宿主如何把危险能力挡在门外。这三节合起来回答"脚本的边界在哪、边界两侧怎么协作"。
阅读完本章,你应当能够:
边界不是墙,是契约:宿主划定能力范围,脚本在范围内自由,出错也有护栏。
模块是 Lua 组织代码的唯一正式单位——一张带公共接口的表。本节拆解 require 的四步加载协议、package.path 的搜索规则、loaded 缓存的双刃(防止重复加载,也挡住重载——热更新要绕的正是它),并给出模块的标准写法与命名建议。
脚本会错,宿主不能倒。本节讲抛错(error 的层级参数与错误对象)、断言(assert 的双面人生)、兜错(pcall 与 xpcall 的分工)、定位(debug.traceback),组装出"宿主安全调用脚本"的标准防护壳——这是第 8 章四大现场共用的错误边界协议。
九个标准库家族过一遍辖区与高频函数,然后进入本章压轴:沙箱的三层构造。宿主怎么没收 io 与 os、怎么用环境白名单收权、Redis 的沙箱具体禁了什么——"自带的家当"与"被没收的家当"之间,就是寄宿关系的全部细节。
一句话论点:模块决定代码的组织边界,错误处理决定故障的传播边界,标准库裁剪决定能力的授权边界——三种边界,一个主题。
6.1 组织边界:require · 缓存 · 模块范式 │ ▼ 出错的模块怎么办 6.2 故障边界:error · pcall · traceback → 宿主安全调用壳 │ ▼ 错误壳之外还要收权 6.3 授权边界:九库巡礼 → 沙箱三层 → Redis 现场清单 │ └──→ 第 8 章 四大宿主复用本章的壳与边界 └──→ 第 9 章 热更新要操作 require 的缓存
_ENV 机制为什么是沙箱的基石? 因为全局访问在词法上就是对环境表的索引——换一张表就是换一个世界。白名单不是"禁止访问危险品",而是"危险品根本不在房间里",存在清单比禁止清单便宜且彻底。
错误在 Lua 里是异常还是返回值? 都是,而且是同一种东西:error 抛出的是"值"(字符串或表),pcall 把它接成"返回值"(false 加错误值)。每层显式决定兜住、加工或上抛,没有隐式传播链——这让宿主能在 C 边界稳稳接住一切。
模块为什么保证只执行一次,这件事的利与弊? 利:顶部是天然的单次初始化区(连接池、常量表),跨调用共享零成本;弊:想重载必须手动摘缓存——第 9 章热更新的第一颗螺丝,也是"文件改了不生效"这桩新手悬案的全部真相。
__tostring(错误对象的自述)。三句话带走本章:
**第一句:组织、故障、授权,三种边界一个主题。**模块把代码切成可管理的单位,pcall 把错误挡在宿主门外,白名单把能力锁进房间——边界越清晰,寄宿越长久。
**第二句:错误是值,不是事故。**error 抛出的是值、pcall 接住的是值、调用方按 code 分支的还是值——错误处理从"惊吓"变成"协议",宿主就永远稳。
**第三句:沙箱是存在清单。**io、os、debug 不在环境里,对脚本而言就不存在——比一万条禁令便宜且彻底;预算防线(指令计数)补上时间维度,死循环也杀不死房东。