我在阅读Ricardo Gerardi撰写的非常优秀的 《Go中强大的命令行应用程序》时遇到了这个错误
下面的startInterval 函数包含一个嵌套的periodic 函数,该函数将在pomodoro定时器中被调用。很好!
问题是,当应用程序运行时,没有任何东西更新应用界面。就像计时器没有运行一样,但如果我暂停并启动计时器,应用程序的用户界面就会更新一次,然后保持冻结。怎么了?
调试(为什么是的,我是PRINTS调试器)显示,periodic 函数确实按照设计被调用,由ticker通道每秒钟获得一个值。问题出在哪里?你能看到它吗?
startInterval := func() {
i, err := pomodoro.GetInterval(config)
errorCh <- err
start := func(i pomodoro.Interval) {
message := "Take a break"
if i.Category == pomodoro.CategoryPomodoro {
message = "Focus on your task"
}
w.update([]int{}, i.Category, message, "", redrawCh)
}
end := func(pomodoro.Interval) {
w.update([]int{}, "", "Nothing running...", "", redrawCh)
}
periodic := func(pomodoro.Interval) {
w.update(
[]int{int(i.ActualDuration), int(i.PlannedDuration)},
"", "",
fmt.Sprint(i.PlannedDuration-i.ActualDuration),
redrawCh,
)
}
errorCh <- i.Start(ctx, config, start, periodic, end)
}
问题是,periodic 函数接受了一个pomodoro.Interval ,但没有把它分配给一个变量。类型匹配(万岁,小类型的胜利),但没有任何东西确保或要求类型被分配。
在periodic 函数中,i 的值默默地指向在外层函数范围内创建的i:
i, err := pomodoro.GetInterval(config) // <-- this i
这意味着该函数起作用了,并且确实在适当的ticker间隔中被调用。但是,应用程序的用户界面实际上从未看到变化,因为i 内的值都保持着间隔开始时的样子。
如何解决?只需改变一个字符,将pomodoro.Interval 赋值给它i:
periodic := func(i pomodoro.Interval) {
w.update(
[]int{int(i.ActualDuration), int(i.PlannedDuration)},
"", "",
fmt.Sprint(i.PlannedDuration-i.ActualDuration),
redrawCh,
)
}
有了这个改动,书中的代码例子就能如愿以偿。
一个更好的方法:不要影射变量名
对我来说,更好的方法是完全不影射外部的i 值,而是给periodic 函数自己的变量名,作为它所期望的时间间隔:
periodic := func(pomodoro.Interval) {
w.update(
[]int{int(activeInterval.ActualDuration), int(activeInterval.PlannedDuration)},
"", "",
fmt.Sprint(activeInterval.PlannedDuration-activeInterval.ActualDuration),
redrawCh,
)
}
有了这个改变,那么Go编译器就会立即嚷嚷出这个问题:没有任何东西设置了activeInterval!很容易解决:
periodic := func(activeInterval pomodoro.Interval) {
w.update(
[]int{int(activeInterval.ActualDuration), int(activeInterval.PlannedDuration)},
"", "",
fmt.Sprint(activeInterval.PlannedDuration-activeInterval.ActualDuration),
redrawCh,
)
}
替代方案
Go 可以对未分配的函数参数发出警告。它甚至可以要求函数参数被分配。在这种情况下,如果你只想匹配签名,并且真的不在乎实际使用函数参数本身,那么你可以把它赋值给_ ,这在Go中明确是一个 "忽略这个变量 "的声明。
更棒的是将其提升到Elixir级别,它允许给被忽略的变量一个名称,以表明被忽略的内容,例如_interval