/writing/posts/debug-experiment

Debugging is Experimenting

D

May 10, 23 / 119 words / in ChitChat

#zh-Hans-CN

在 Telegram 的 Rust 群里经常会有群友问出各式各样的问题,其中有很大一部分在我看来是可以通过更简单直接的方法解决的。前段时间简单的总结了一下这类问题和解决方法,今天展开来说说。

TL;DR

  • 获取信息
  • 提出假设
  • 试错
  • 重复

Debugging is Experimenting

这句标题的意思是,在我看来,调试的过程实际上很像是做实验——或者更普遍的——做研究的过程。在遇到一个问题时,你首先应该做的是尽量收集信息,接着提出假设并验证假设,如此重复直到得到正确答案。

考虑这个例子(这是一个今天在群里发生的现实中的例子):

你正在使用 Rust 写一个程序,其中用到了 Regex 这个库。现在代码中有这样一段:

let url_pattern = r"^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$";

let re = Regex::new(url_pattern).unwrap();
re.is_match(url)

这段代码在你家里的电脑里运行良好,但是当你把代码放到公司电脑上运行的时候,却总是报错:

regex parse error:
    ^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$
                  ^^
error: unrecognized escape sequence

你感到非常困惑。这时你想起来也许谷歌能帮到你,于是通过搜索找到了这篇 Stackoverflow上的问题。根据回答者的说法,Rust 的 Regex 库并不支持对于 / 的转义,所以你需要把正则表达式中的 \/ 改成 /。但是你仍然不理解为什么自己的电脑上能够正常编译运行。作为老生常谈的一步,在提出问题之前你想到你应该首先试图在所有相关的地方搜集信息。其中,Github 的 issue 区乃是重中之重。于是你在 Regex 库的 issue 区里搜索了一下,发现了这个 issue。这是一个五年前年的 issue,目的在于允许一些无效的转义字符,并且在三个月前被合并后随 Regex 1.8.0 版发布。

现在,你已经获得了足够的信息——你了解到了为什么你的代码会报错,也知道了你的代码为什么不会报错。但问题依然存在,也即,为什么同样的项目配置在不同的电脑上表现出的行为不一致?你提出了假设:

因为不明原因,公司电脑仍在使用 1.7.x 版本的 regex,但家中电脑已经升级到了 1.8.0 版本。

你证实了自己的猜想:

$ cargo tree -i regex
regex v1.8.1

使用 cargo tree 获取由上至下的依赖树,并使用 cargo tree -i $SPEC 获取由下至上的反向依赖树。

但,由于缺课的你没有认真阅读 Cargo 文档,你并不知道 Cargo.toml 中如果依赖版本不加符号默认行为是 ^ 而不是 =,这与 npm 不同。现实中,群友在这里为你指出了这个问题。但假设你没有这么幸运,你会怎么做呢?没错,继续收集更多的信息。以下都是在软件开发中常用的获取信息的方法:

  • 继续尝试不同的参数/环境/配置并观察行为
  • 阅读文档
  • 搜索 Github issue
  • 搜索 Stackoverflow
  • 搜索 其他论坛(包括 reddit 或 rust user forum 等)
  • 阅读代码(实现及注释)
  • 使用各类调试工具

When experimenting is not enough

但,有些时候,即使你已经尽力收集了所有的信息,你仍然无法解决问题。这时,你需要尝试一些其他的方法。其中,咨询他人是最常用的方法之一。关于如何正确的提问,也已经老生常谈。但,实际上,询问他人并不是一种「高效」的方法。这需要他人恰好了解你所遇到的问题并愿意回答。通常来讲,大部分问题都是通过上述方式即可自行解决的。学会什么时候提问,什么时候自己解决问题能够极大的提高效率。