Go run

Go 的 `go run` 命令常被拿来说明,简洁的工具链如何让编译型语言几乎像脚本语言一样使用,尤其是像 `go run .` 这样的约定,以及自动依赖获取。评论者将其与 JavaScript/TypeScript 生态对比,后者多种运行时、模块系统和包管理器会让基本任务显得更复杂,不过 Deno 和 Bun 等新工具正在缩小这一差距。这场讨论凸显了语言设计、错误处理、构建系统和依赖管理中,简洁性与灵活性之间的更大权衡。

go run 的用法与约定

  • 许多人指出,一旦 main 拆分到多个文件中,go run main.go 就会失效;go run . 会运行当前包,并且可以处理多个文件和子目录(go run ./cmd/foo)。
  • 也有不少人抱怨教程强调 go run file.go,而不是更简单、也更具可扩展性的 go run .
  • 有些人希望不带参数的 go run 能像其他 go 子命令一样自动检测 main 包,但也有人认为在有多个 cmd/* 二进制时会产生歧义。
  • 模块带来了一些阻力:你通常必须位于模块根目录才能 go run .,或者使用 -C 来切换目录。

简洁性、约定与学习曲线

  • Go 因其可预测的布局而受到称赞:目录即包,cmd/ 用于二进制;浏览陌生代码库时往往很直观。
  • 但也有人觉得“简单 vs 复杂”的边界并不直观(例如需要知道何时使用 go run .,何时使用 go run path/to/main.go)。
  • 对于教程和心智模型究竟应该在多大程度上受先前语言经验影响,存在争论。

与 JS/TS 及其他生态的比较

  • 有人将 go run 与 Node/TypeScript 生态做对比,指出其碎片化(npm、yarn、pnpm;CommonJS vs ESM;TS 转译、Jest/Babel 配置)。
  • 也有人反驳说,Node 已经可以运行 ESM(.mjs),也可以通过简单工具链运行 TS(tsc、loader),而且更新的工具(Deno、Bun、tsxnpx)也让 TS 脚本同样容易。
  • 还提到了 Rust 的 cargo run、Python 工具(Poetry)、Nix 和 Bazel,作为类似的“构建 + 运行”流程。

依赖获取与安全性

  • 一个被强调的特性是:go run 会自动从模块路径下载依赖。
  • 支持者喜欢这种低摩擦的工作流,认为版本由 go.mod/go.sum 锁定,并由 Go 代理支撑。
  • 批评者认为,在 run 过程中自动获取依赖是安全/运维上的反模式,更倾向于明确的“先获取,再运行”步骤,尤其是在首次下载时。

对 Go 的更广泛看法

  • 许多人称赞 Go 的工具链(单一工具链、静态二进制、交叉编译、简单部署;有时甚至不需要 Docker)。
  • 也有人批评 Go 的错误处理过于冗长,以及与 Rust 或 TypeScript 相比缺少高级类型特性,不过也有人欣赏其显式性。
  • Go 被看作介于底层的 Rust 和高层的 TS 之间,很适合后端服务和小型实用工具,但并不一定能满足所有人的语言偏好。