与 Rust 在一起的四年

purplelion 发布于8天前 阅读573次
0 条评论

本文翻译自 Steve Klabnik 的 博客 。Steve 是 Rust 核心团队成员,主要负责文档撰写和社区建设。这篇文章是 Rust 0.5 发布公告的一个考古文,对比着今天的 Rust 看还是蛮有意思的

———— 分割线 —————-

从我第一次听说 Rust 语言到今天已经是第 4 个年头了。之所以还记得这个日子是因为我用的第一个 Rust 版本是 0.5。这四年来 Rust 发生了很大的变化。关于发展历史概况可以观看 我这个演讲 。但是我觉得今天回过头来去查查当时的 发布公告 ,看看哪些东西发生了变化,哪些东西仍然保留会很有意思。

rust-dev

首先说说公告本身, rust-dev 邮件列表 曾经是 Rust 存档形式的一站式讨论聚焦地。不过在 2015年1月 ,我们决定关掉它。为什么呢?让我们回顾一下 Brian 当时说的

你很可能已经注意到了,最近几个月 rust-dev 邮件列表流量大幅下降。这最初是项目协调的自然结果,继而我们又刻意逐步废掉邮件列表。

项目最初用来讨论 Rust 的只有 #rust 频道和 rust-dev 邮件列表两个地方,并且多年来我们一直很开心地用这两种交流方式。随着项目的发展,项目协调也被转移到不同渠道,大家在许多不同的地方交流,于是 rust-dev 邮件列表的目的变得不那么明确了。与此同时邮件列表里面也出现了许多白热化不受管理的撕逼讨论,导致许多人对邮件列表的有效性失去了信心。

我喜欢邮件列表,但我知道在这方面我比较独特。但邮件列表的确有一个很大的问题,管理员的管理功能很弱。你当然可以禁言某人,但这就是你所能做的全部管理了。不能只删除某些帖子,不能设置冷静时段,不能 shadow banning(译注:被禁言者依然可以发言,不过其他人看不到)。尽管 Rust 社区在管理上面广受赞誉,但绝不代表一切都很完美。我们艰难地从经验中吸取教训。

并且 mailman 在某种意义上是开源界的“老土货”,大家把它看作落后过时的东西。现在很少有人想用邮件,文化在发生变化,我们意识到这一点,所以决定转向 Discourse 。rust-dev 以 Discourse 的 users internals 形式幸存下来。这两个版块的明显区别是,前者讨论 Rust,后者讨论构建 Rust。Discourse 也同样是自由/开源软件,但是却有更好的管理工具,甚至你仍然可以让它给你发邮件。

我们也不会在这些论坛里做版本发布公告,我们有专门的 博客 发公告。

让我们来818那次的发布注记!

900 个变更,大量的 bug 修复

900 个变更相当多!那时 Rust 基本上是6个月的发布周期,所以意味着每天有 5 个 PR 被合并。Rust 1.14 在6周的发布周期里 会有 1230 个 PR,即每天有超过 29 个 PR。这是非常非常快的发展速度。而且这只算了我们的主仓库,我们已经加了更多的仓库,比如 Cargo crates.io 。实际上这也是我很长一段时间以来的困扰,我们只在发布公告里表扬了给 rust-lang/rust 贡献过代码的人,基于这段历史,现今发生了更多的事情。我想给 Rust 项目做一份像 Rails 贡献者 一样的列表,谁愿意抽时间跟我一起来做这个工作?

语法变化

  • 删掉 move 操作符 <-

Rust 在生命周期”什么时候 move ,什么时候 copy ”的语法表示上经历过好多次迭代。如今是通过看类型是否实现了 Copy 来做区分的。但是过去 Rust 有多种形式,我用的第一个版本的 Rust 移除了这个语法,尽管我记得不大清楚,但我想应该是下面这样的

x <- y; // move
x = y; // copy

我相信 Rust 还曾用过下面这种形式

x = move y; // move
x =  y; // copy

我们决定在语法上统一这两种形式的两个理由是:第一,标注看起来是比较繁重的活,如果你弄错了,编译器会告诉你应该用 move 还是 copy。第二,至少在如今的 Rust 里 move 和 copy 是等价的操作,除了在这两个操作之后,你能否继续用旧变量这一点区别之外。而让 move 和 copy 操作使用相同的语法则强化了这一点。

Niko 的博客里有很多很赞的历史细节, 关于这个话题也不例外

  • 完成了从 #fmt 扩展语法到 fmt! 的彻底转换

很久以前 Rust 有一条规则 “关键字不能超过 5 个字母”,所以很多词都被简写了。这个特殊的语法扩展延续到今天,只不过换成了更长的名字format!

  • 移除了原来固定长度向量的语法 - [T]/N

我们今天所知道的 Rust 里面的向量和数组类型经历过很多内部迭代。[T]/N 在今天的等价形式是 [T; N],但是我敢肯定的是这两种表示方式也有些细微的差别,不过我不大记得了。

  • quasi-quoter 的新语法 quote_tokens!, quote_expr! 等

这些在某种意义上仍然存在,不过始终没有稳定下来,以至于根本没有出现在官方文档里面,不过 Manish 维护着一份文档拷贝 。这些是“语法扩展”的工具,属于 Rust 元编程最强有力的形式。它们的设计终稿 8天前才被接收 ,所以等到在稳定版 Rust 见到这个语法还要等好一阵子。

  • 宏现在可以延拓到 item 和 statement 了

尽管我不知道为什么之前宏不能这样做,但是现在却是可以的,而且也非常有用。

  • a.b() 总是被解析成方法调用而不是一个字段

这是一个有意思的边界情况,下面这段代码展示了这一问题:

struct Env<F: Fn(i32)> {
    f: F,
}

let e = Env { f: |i| println!("Hello, {}", i) };

e.f(); // what does this do?

根据这一条注记,这里会是一个方法调用。但是如今却会是一个错误,你需要写成 (e.f)(); 才行,不过至少错误提示可以告诉你该怎么做

error: no method named `f` found for type `Env<[closure@<anon>:6:22: 6:50]>` in the current scope
 --> <anon>:8:7
  |
8 |     e.f(); // what does this do?
  |       ^
  |
note: use `(e.f)(...)` if you meant to call the function stored in the `f` field
 --> <anon>:8:7
  |
8 |     e.f(); // what does this do?
  |       ^
  • 通过 #[deriving_eq] 和 #[deriving_iter_bytes] 会自动生成 Eq 和 IterBytes 的实现

我们现在有更通用的方法派生 trait:例如 #[derive(Eq)]。Eq trait 现在仍然存在,而 IterBytes trait 则已经不存在了,我不大记得这个 trait 是干啥的了。

当前哪些 trait 可以派生仍然只能由编译器决定的,对于一些像 Serde Diesel 这样实用的库,这也成了大家用 nightly 版本的一个重要原因。但是随着 1.15 版本的到来,这一限制将会放宽,大家使用稳定版 Rust 的一个最大障碍将会被消除!:confetti_ball::confetti_ball::confetti_ball:

  • 移除了 .rc 文件的特殊 crate 语言(译注:应该是之前 crate 文件需要手动用 DSL 来描述)

今天与 .rc 文件等价的是 .crate 文件,这些文件会被 Cargo 上传到 http:// crates.io 。但是与 0.5 时代不同的是,现在你几乎从来不用关心这些,因为 Cargo 使之能工作。

  • 函数参数可以由 irrefutable 模式构成

的确如此,而且也是大家所知道的一个事实!像下面这样的:

struct Point {
    x: i32,
    y: i32,
}

fn takes_point(Point {x, y}: Point) {
    println!("({}, {})", x, y);
}

fn main() {
    let origin = Point { x: 0, y: 0 };
    takes_point(origin);
}

即参数对于函数来说是 PATTERN: TYPE 而不是 NAME: TYPE,在上面的 takes_point 函数中作用域内的是 x 和 y 而不是整个 point。

语义变化

  • & 和 ~ 指针可以指向对象

Rust 很久以前的确是有对象的,不过我认为我接触 Rust 时就已经移除了,我不大清楚这一点。

值得一提的是 ~ 现在变成 Box<T> 了,不过这又是一个很长的故事了。。。

  • 元组结构 - struct Foo(Bar, Baz) 替换了 newtype 枚举

当前的 Rust 中 依然有这些。

  • Enum variant 可以是结构

现在也是如此

enum Foo {
    Variant { x: i32, y: i32 },
}
  • 析构可以通过 Drop trait 加到所有标称类型(nominal type)

现在也是如此

我觉得这里的 “nominal” 暗指 Rust 类型系统里面比较老的一些东西,如果没记错的话,现在 Rust 已经不再区分这些了,所有类型都是 nominal 的,不过我不敢 100% 肯定。

  • 结构和空 enum variant 可以是常量

现在也是如此

  • 不能隐式拷贝的值不用显式写 move 也会被自动 move

我在上面谈 <- 时也提到过这点

  • &T 现在可以被强制转换成 *T

现在 *T 可以是 *const T 或者是 *mut T,不过这条注记也是对的:

let x = &5;
let y: *const i32 = x;
  • let 语句和函数调用里会发生强制类型转换

多年来我们在“什么时候进行强制类型转换什么时候不转”上经历了许多不同的迭代。实际上我期望上面的例子中 *const i32 必需要有个 as 来做转换。

  • use 语句现在取的是 crate 相对路径

现在也如此。我真的是很喜欢这一点,但是很多人发现这个很迷惑。基本上 “use” 总是从 crate 的根开始,所以有下面的

mod bar {
    struct Foo;
    mod baz {
        use bar::Foo; // from the root
        use super::Foo; // if you want to start from the parent
        use self::super::Foo; // self makes it be relative. Not even sure if self + super works, but you get the idea.
    }
}
  • 模块和类型的名字空间合并了,以便静态方法的名字可以在定义的 trait 里被查找到

这一条不太确定,也有点野生。

改善了语言特性的支持

  • trait 继承可以在许多场景下工作

用继承这个词一直有点不恰当,这里继承是指

trait Foo: Bar {
}

如果你想实现 Foo 的话,你必须还得实现 Bar,就是这么回事。这一行让我感到很愉快:“这个解释比继承更好”

  • 支持方法里显式的带上 self 参数 - self, &self, @self 和 ~self 这些都如预期的一样工作

现在的 Rust 不仅仅支持这么做,而且是强制要求带上的,不过

  • self 仍然是 self
  • &self 也仍然是 &self
  • @self 类似于 Rc<self>
  • ~self 则变成 Box<self>

@self 本来是用来表示 GC 类型的,但却除了用来引用计数外从来没有成为 GC 类型

另外后面两种也不是那样写的,而是 self: Rc<Self> 和 self: Box<Self>。 后者可以编译,而 Rc 那个却不行,因为 Box 有黑魔法。不过这一点最终会被修正过来。

  • 静态方法可以在更多的情景下工作

又一行“哇,更多的特性可以工作”。:)

  • 实验特性:Trait 可以定义默认的方法给 impl 用

如今也是,而且这一点很有用。考虑Iterator:所有的方法都有默认实现,所以你只需要定义 next(),其它的方法就可以自动获得了

  • core::condition 中新的 condition 处理系统

condition 是 Lisp 用来处理错误的方式,Rust 曾经支持过一段时间。这一点非常酷,但是没人知道如何有效的使用,所以也就没有人去用,于是乎现在就消失了

  • Timsort 被加到 std::sort

std::sort 现在已经不存在了,尽管我们有 slice 排序函数 。我们不再声明是某种特别的算法,而只是说“这个排序方法已经稳定了,并且在最坏情况下是 O(n log n),但是需要分配大约 2*n 的空间,其中 n 是 self 的长度”。

最近这个算法得到大量的改善 ,这些工作还是一个初次贡献者做的,简直干得太漂亮了。在这个 PR 里,他是这样解释这个算法的:

然而如果我们从 TimSort(排序时智能归并策略)中汲取主要观点并且放弃 gallop,则对于随机输入可以获得极高的性能,而且对于部分排序的输入也不会太差。

TimSort 仍然在那并发挥着巨大的作用。

  • 新的优先级队列:std::priority_queue

如今是 std::collections::binary_heap

  • 针对可序列化类型的管道: std::flatpipes

不知道如今这个东西去哪里了

  • 序列化大幅修改变成基于 trait 的了

如今也是。见上面提到的 Serde,不过 rustc-serialize 算是作弊,编译器可以理解 RustcEncodable 和 RustcDecodable。对于 1.15 的到来迫不及待!

  • 扩展了 getopts 定义

我们已经把 getopts 从标准库源码树中移除了 ,不过这个库仍然健在

  • 将 futures 移到 std

卧靠!我都忘了我们曾经在标准库里是有 futures 的。Futures 是现在 Rust 社区里最火的话题之一。 Tokio 是 Rust 圈里有史以来最受期待的发布之一。想尝鲜的可以看看这个 演讲 。一个哥们告诉我 Tokio 很快就会发布 0.1 了。。。

  • 有更多的纯函数了

Rust 已经没有纯函数的概念了,尽管 const fn (仍然是 unstable)在某种程度上算是纯函数。关于 Rust 纯函数的历史可以看看 Niko 的博客 ,或者是 Graydon 关于为什么纯函数被移除了的解释

  • core::comm 重命名为 oldcomm,仍然是废弃的

很早很早以前就消失了

  • rustdoc 和 cargo 现在变成库了

Rustdoc 仍然存在,不过不是以库的形式。Cargo 则已经不是你所想的 cargo 了, 完全是另一个东西 。在当前的 cargo 之前,Rust 的包管理经历过许多次迭代。例如曾经有 rustpkg ,出现在这里提到的 Cargo 之后,今天我们用的 Cargo 之前。

杂项

  • 初步增加了 REPL:rusti

我们现在从 Rust 源码里移除了 repl,因为 repl 从来没有真正起作用过,而且还是代码维护的噩梦。 这个 是当前我所知道的唯一一个。一些人要求提供一个,不过没有人一起来做这件事去实现一个好用的 repl。这是一件很艰难的事情!

  • 许可证从 MIT 变成了 MIT/APL2 双许可证

现在也是如此,也没有计划改变这个。

Rust 0.5 的贡献者:

Rust 0.5 版本有 41 个贡献者,而 1.14 则有 144 个,这是这些日子以来的每版的平均贡献者人数。这 41 个人中,我粗略看了下,认出了其中 17 个人的名字,其中大概有 6 个依然参与着当前 Rust 和 Servo 项目,另外 11 个人呢?一些是实习生现在已经在其它地方工作了,所以不能继续为 Rust 工作了,一些贡献者由于不同的原因离开了,自然包括 Graydon。

在发现 Rust 语言后第六天我提了我个人的 第一个 PR 。你会注意到这个 PR 没有被合并,原因是操作失误,我把它发到错误的分支上去了!现在 GitHub 允许你修改这样的失误,但是我当时又开了 另一个 PR

我在那个 PR 里面是这样说的:

我刚刚开始使用 Rust,并且非常喜欢它。其中一个让 Rust 显得很困难的是文档的缺乏,当然这没什么大问题,毕竟 Rust 还没有达到生产可用。

我愿意帮助改变这一点。

作为开始,我只是修改了 rustdoc 里面这一段短短的描述,如果这个有帮助,并且你们都喜欢我的修改,我将会按这种方式修改 core 的其它部分文档,继而可能修改 stdlib 的文档。

你也许会说的确起作用了,我现在是 核心团队 的一员了。从那之后我已经提交了 866 个 PR,第一个 PR 在 Rust 0.6 版本里 ,而 Rust 0.10 是唯一一个我没有提 PR 的版本。而现在我负责写 Rust 发布公告。

我希望你喜欢这一小段记忆航线。如今我依然喜欢 Rust 及其社区,这是给未来更多年的:flushed:♥

查看原文: https://zhuanlan.zhihu.com/p/24781740

共收到0条回复

需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。