用 map[string]bool 易混淆“未添加”和“显式设为 false”,应改用 map[string]struct{} 或 task 结构体;小工具初期用 flag,多子命令必选 cobra;json 适合原型,数据量大或需查询过滤时应用 sqlite。

为什么用 map[string]bool 存待办状态容易出错
Go 里很多人一上来就用 map[string]bool 表示“是否完成”,看似简洁,但实际会漏掉“未添加过的任务”和“显式标记为 false”的语义区别。比如用户刚输入 "买牛奶",还没点完成,这个 key 根本不在 map 里;你查 done["买牛奶"] 得到的是零值 false,但它其实不是“已取消”,而是“不存在”。
实操建议:
- 改用
map[string]struct{}存已完成项(节省内存,且存在即完成) - 或定义结构体:
type Task struct { Text string; Done bool },避免零值歧义 - 如果用
bool值,必须配合ok判断:if done, ok := m["task"]; !ok { /* 未创建 */ }
命令行参数解析该选 flag 还是 github.com/spf13/cobra
小工具初期用标准库 flag 完全够用,但一旦要支持子命令(如 todo add、todo list --done),flag 就开始反人类:手动分发、重复写 Usage、无法嵌套、没自动补全。
实操建议:
- 单命令(如只跑
todo -l)→ 用flag,5 行搞定 - 两个以上子命令或需要 help 分组 → 直接上
cobra,别硬撑 -
cobra初始化后,rootCmd.AddCommand(addCmd)比手写switch os.Args[1]稳定得多 - 注意
cobra默认开启completion,上线前记得关掉或明确导出:rootCmd.CompletionOptions.DisableDefaultCmd = true
保存数据时用 JSON 文件还是 SQLite
JSON 文件适合原型验证,但并发写入、部分更新、查询过滤(比如“查所有未完成”)会逼你把整个文件读进内存再遍历——几百条还行,上千条就卡在 os.ReadFile 和 json.Unmarshal 上。
实操建议:
- 纯本地 CLI、无并发、数据量 os.WriteFile("tasks.json", data, 0644) 足够
- 要加搜索、排序、状态筛选、或多进程可能同时操作 → 上
github.com/mattn/go-sqlite3,哪怕只建一张表 - SQLite 不用单独装服务,但要注意路径权限:
sqlite.Open("todo.db")如果目录不存在会报no such file or directory - 别用
fmt.Sprintf拼 SQL 查询条件,用参数化:db.Query("SELECT * FROM tasks WHERE done = ?", done)
time.Now().Format("2006-01-02") 为什么总错
Go 的时间格式化不是用常见占位符(如 %Y 或 YYYY),而是用一个具体日期 Mon Jan 2 15:04:05 MST 2006 的各字段值当模板。写成 "2006-01-02" 是对的,但写成 "YYYY-MM-DD" 或 "%Y-%m-%d" 就会原样输出字符串,不解析。
实操建议:
- 记不住?直接查官网文档页右上角那行“reference time”
- 想带时区?用
time.Now().In(loc).Format(...),别依赖本地时区,否则部署到服务器可能差 8 小时 - 存数据库时优先用
time.Time类型字段,而不是字符串;SQLite 用TEXT存 ISO8601(t.Format(time.RFC3339))更安全
事情说清了就结束。真正卡住人的,往往不是语法,而是“任务状态怎么建模”和“文件写入时要不要加锁”这种边界判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











