9
28
2018
20

每次修 Python 代码的 bug 的时候总会想念 Rust

本文来自依云's Blog,转载请注明。

俗话说:由俭入奢易,由奢入俭难。

之前写 Python,老是在实现完一个特性之后,弄出来几个 AttributeError: 'NoneType' object has no attribute 或者 TypeError: list indices must be integers or slices, not str,还有 TypeError: can only concatenate str (not "int") to str 这样的错误。一看就明白自己又是哪里一不小心疏忽了,稍微修一下就好。

后来啊,我遇见了 Rust,整个流程就变了。之前写的时候,基本上都是通过手动测试来发现这种问题。为了高效、不破坏性地测试,需要控制测试的数据量,需要保证出错的时候相关的数据不会处于某种中间状态。当然在服务器上跑的脚本,我还要来来回回地传更新的脚本,或者弄个本地测试环境。而这一切,可能不过是为了跑一个成功之后再也不会用到的小程序,比如之前分析抓包数据的那次。而在 Rust 里,这些最容易犯的错误,cargo check 一下,编译器基本上能全给你指出来。所以有时候写一些小工具我也用 Rust,虽然写起来慢,但写好就能正常运行,不用反复试错,多好啊!

最近给 Arch Linux 中文社区的自动打包机器人 lilac 增加新特性。结果实现完部署之后,夜里就被 lilac 叫起来修 bug 了,还一下子就是仨……(lilac 很难本地测试,而短暂地服务中断又没多大影响,所以我都是不进行本地测试的。)

第一个 bug 是,与 dict.get 不一样,getattr 是没有默认值的。Python 里这种不一致很多,比如 configparser 里默认值要用关键字参数指定。Rust 遇到类似的情况,就会返回一个 Option。或者如果 API 决定如果不存在就 panic 的话,那么它就会直接返回我要取的值的类型,而不会包一层 Option。而我后边的代码是预期到这里可能取不到那个属性的,所以弄错了就会类型不匹配。

第二个 bug 是局部变量在一个分支上没有初始化。Rust 当然不会允许这种情况了。实际上 C 都不用担心这种问题,编译器会给出警告的,还有一些 linter 可以用。而 Python,很遗憾的是,我所使用的 pyflakes 并没有对此发出警告。我当然知道 pylint 那些。我很讨厌 pylint 和 jslint 这种不区分潜在 bug 和风格问题的 linter。我只需要工具在我可能疏忽的时候提醒我,而不需要它对我的编码风格指指点点,特别是那些指指点点往往是不对的。比如我的文件描述符变量名不叫 fd 难道要叫 fildes?

第三个 bug 是一个可能为 None 的变量我忘了先作 is not None 判断。这段代码如果初写的话我肯定是会注意到的,但是改的时候,只想着如果 pkg 里有冒号我得处理一下,就忘记了根本没有关联的包名的情况。Python 的 None,以及 C 和 C++ 的 NULL、Java 的 null、Lua 和 Ruby 的 nil、JavaScript 的 undefined 和 null,被称作是十亿美元错误,给无数程序员和用户带来了无尽的 bug。幸好这个东西在 Rust 里不存在:表达「没有值」的值没有被作为特殊值存在于几乎所有类型中,而是作为一类类型的可能的值之一。想要使用「正常」的值,就需要显式地进行类型转换,所以不可能被不小心忽略掉。顺便说一下,Go 里也有 nil 这种东西,以至于会出现这种不容易发现的 bug

Python 现在也给出了解决方案:类型注解,提供类似的类型检查。不过检查器是第三方的,也并不十分完善。等我找到机会试用过之后再来写感想啦。

Category: python | Tags: python 编程 编程语言 Rust | Read Count: 24361
x.b.y 说:
Sep 29, 2018 10:33:06 AM

VC++忠实用户,重构开源项目几十个文件,编译问题解决以后一次运行成功。
python只能依赖测试了。typescript也比python的检查机制强一些

Avatar_small
依云 说:
Sep 29, 2018 11:27:26 AM

不错啊,现代 C++ 应该也很强大了。

zhenbo_endle 说:
Sep 30, 2018 11:01:22 AM

看了很多季的ACI,越来越坚信,如果一个工具的使用者很容易犯错误,那问题一定出在工具本身的设计上。
(但是我还是没能抽出时间看rust)

黑星 说:
Oct 01, 2018 08:56:13 PM

已经被rustc惯坏了, 修改了以后不是仔细检查, 而是坐等编译错误

mathematrix 说:
Oct 08, 2018 10:17:05 AM

现在新写的(包括改之前的)Python代码基本都会加类型注解,主要是IDE(PyCharm)可以做很多自动检查和提示,还是方便不少

Avatar_small
Veritatis 说:
Oct 13, 2018 03:38:18 PM

fildes,哈哈,Alice 粉?

Avatar_small
依云 说:
Oct 13, 2018 10:52:38 PM

哎,这个跟 Alice 有什么关系呀?
我是在 man 手册里遇到这个变量名的,当时还想了好久这是啥意思。

nwindy 说:
Nov 12, 2018 10:57:08 PM

动态类型一时爽...一点都不爽好不好, 静态语言都有类型推导...
所以快用 Haskell, Scala, C#!
Type hint 是邪教!

Avatar_small
依云 说:
Nov 13, 2018 03:38:19 PM

Haskell 的报错根本看不懂,想要运行效率高也不容易呢。(所以还是选择 Rust 啦。)

nwindy 说:
Nov 13, 2018 11:32:53 PM

觉得Rust和Scala特别像
其实这三个Bug全是因为Python 类型系统太烂(或者说根本没有...)

nwindy 说:
Nov 13, 2018 11:37:12 PM

话说百合仙子你怎么看Manjaro?

tiankonguse 说:
Nov 14, 2018 12:33:24 AM

记得现在的lint 支持指定使用哪些规则进行扫描的

Avatar_small
依云 说:
Nov 14, 2018 09:41:47 PM

pyflakes 不支持任何配置的。pylint 支持,但是我要改的规则太多了,再加上它比较慢,就懒了……目前在用 mypy,效果还不错,有时候我会写篇文章。

Avatar_small
依云 说:
Nov 14, 2018 09:45:39 PM

我不看 Manjaro 啊。很早以前在虚拟机里装过,挺漂亮的,然后就没有后续了。

Manjaro 的存在本身我觉得挺好的,毕竟是继承了 Arch 的包管理器,也是对 Arch 的支持了。然而用户总是把它当 Arch 跑来论坛和群里问问题,有时候还不说明白自己用的是 Manjaro,很烦。我觉得 Manjaro 可以在安装器里加行大字:「本发行版衍生于 Arch 但并不是 Arch,请不要去 Arch 社区求助」。

nwindy 说:
Nov 23, 2018 01:31:10 AM

Manjaro 那个 Manjaro-architect 安装器挺好的,
我觉得Arch 也应该搞一个(虽然 pacstrap 已经不错了)

酒酿 说:
Feb 11, 2019 12:58:42 PM

经测试,listdir和scandir同时遍历一个900张图片的目录用时分别为0.001234秒和0.00038秒.如果打印返回结果,分别为0.0128和0.0015秒,但listdir返回的是列表,scandir返回<nt.ScandirIterator object at 0x000001532C36A2F0>对象.这对打印耗时影响很大.python对列表的迭代也很慢.

酒酿 说:
Feb 11, 2019 01:24:57 PM

点错文章了,抱歉

青藤木子 说:
Feb 12, 2019 01:14:32 PM

一用rust一时爽,一直用rust一直爽,rust真的好用

haijun 说:
Feb 05, 2024 04:02:40 PM

我想把RUST和ARCH合并,(邪教双并),并合上ZORIN,(

haijun 说:
Feb 05, 2024 08:54:04 PM

我会做到的。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

| Theme: Aeros 2.0 by TheBuckmaker.com