【在主画面加入捷径】
       
【选择语系】
繁中 简中

Rust 程序设计教学:基础概念

【赞助商连结】

    在本章,我们对程序 (program) 及程序设计 (programming) 进行一些概念上的说明;接着,会介绍 Rust 的一些特性以及和其他程序语言的比较。在这里,读者不需要实际撰写程序。

    程序

    电脑程序内预先写好了特定的步骤,在执行该程序时完成特定的任务。程序有很多种,小到某个终端机的指令 (command),大到整个操作系统 (operating system),都是不同种类的程序。除了使用现有的程序,电脑使用者也可自行撰写新的电脑程序。透过撰写电脑程序,使用者不再受限于现有的程序所提供的功能,而可以将某个特定的任务,以自己期待的方式,利用电脑来解决。

    将某个任务转为电脑程序的过程,就是程序设计 (programming),在后文中,会探讨这个议题。在实现程序的过程中,我们会撰写程序代码 (code),程序代码经特定软件的处理后,会转化成程序,这个转化的过程,可透过编译 (compilation) 或是直译 (interpretation) 来完成,而这个特定的软件,就是编译器 (compiler) 和直译器 (interpreter)。

    程序代码会以某种程序语言 (programming language) 来呈现。程序语言和人类平日使用的自然语言 (natural language) 不同,前者需遵循正规的语法规范。学习特定的程序语言的语法也是学习程序设计的一部分。如果以上述的程序代码转化成程序的方式来区分,程序语言大略分为编译语言 (compiled language) 和直译语言 (interpreted language),如果将同样的步骤以不同程序语言撰写,通常编译语言所生成的程序,其效能较佳。本书所要介绍的 Rust 也是一种编译语言。

    程序设计

    程序设计是将某个问题转换成可由电脑执行的程序,以利用电脑解决该问题。程序设计分为以下三个步骤:

    1. 分析问题
    2. 设计相关步骤及设计相关的算法 (algorithm)
    3. 实现 (implementation),就是撰写程序代码

    分析问题需要该问题相关的领域知识 (domain knowledge),例如,如果我们想写一个预测基因体中基因可能存在的位置,我们必需学习相关的分子生物学的知识。程序设计的书,无法对领域知识提供太多帮助,而需要读者另行找寻相关资讯。

    算法是针对某个问题的解决步骤。学习算法的目的,不仅在于设计正确的操作步骤,更要设计有效率的步骤。相较于程序设计,算法是更为抽象的主题。建议程序设计初学者先至少熟悉一种程序语言后,再回头学习算法。本书不会用到深入的算法,而以初阶的程序设计为主。

    实现算法需要使用某种程序语言,要选用那一种语言,有许多不同的考量,像是发布平台、执行效率、开发时间、问题领域、个人偏好等。算法和程序语言是无关的,但是由于实务上的考量,有时候某些程序语言会更适合某些特定的任务,有些则仅是个人喜好而已。本书的目标是让读者学会 Rust 程序设计,日后应用在各种不同的任务中。

    程序的基本组成

    程序的运行过程可能很复杂,但大抵上包含以下数种核心概念:

    • Sequencing
    • Selection
    • Iteration
    • Procedural abstraction, i.e. function, procedure, subroutine
    • Recursion
    • Data abstraction, i.e. class and/or object
    • Concurrency
    • Exceptional handling
    • Nondeterminacy

    在学第一个程序语言时,要同时学习这些概念和程序语言的语法,所以学习过程会较长。第二、三个以后的语言,只要专注在语法上即可,学起来就会快得多。

    撰码风格

    每个程序语言都有自己的撰码风格 (coding style),虽然不造撰码风格写程序也不一定会造成程序的错误,维持良好且一致的撰码风格可以使得程序代码更容易维护。程序代码不仅是要传给编译器或直译器执行,也是要给程序设计者看的,包括未来的自己。如果曾经接手过维护不良的程序代码,还要浪费时间去猜这些程序代码的意图,就会知道撰写良好的程序代码的重要性。从学习程序的早期就开始习惯良好的撰码风格,对日后会有相当的帮助。

    如果读者想学习 Rust 的撰码风格,可参考这里

    Rust

    了解一个程序语言适用的层面,对于是否要在自己的项目中使用这个语言,是有相当帮助的 ─ 毕竟,将项目整个重写的代价实在太大了。接下来,我们会简介 Rust 这个语言,让读者知道为什么 Rust 值得学习。

    根据 Rust 官方网站的说明,Rust 是「安全、快速、共时的系统程序语言」,我们将会逐一讨论。

    在 C 或 C++ 中,使用者有较大的自由,像是可以自由地操作指针和内存,然而,C 或 C++ 不是一个安全的语言,有许多的动作是未定义且危险的,程序设计者需维持高度自律,以避开这些问题。在许多高阶语言中,程序是安全的,但高阶语言的使用者无法像在 C 或 C++ 中那么自由。而 Rust 中,透过严格的编译器,将许多 C/C++ 中相对危险的操作视为程序的错误,但仍保有一些操作指针的自由。严格的编译器使得 Rust 较难上手,以前在程序执行时发生的错误,现在提早到编译时就发现。往好处想,当程序能顺利编译时,该程序的错误也比较少。

    一些评效 (benchmark) 显示,在相同算法的前提下,Rust 程序已有接近等效 C++ 程序的执行速度。在实务中,已经有人用 Rust 撰写游戏引擎 (game engine) (如 Piston) 和操作系统 (operating system) (如 Redox),而这些项目都对效能有较高需求,可以知道 Rust 已有足够的效能和低阶操作,应对不同层面的任务。其中的原因之一,在于 Rust 不依赖垃圾回收 (garbage collection) 来管理内存,而是使用类似 C++ 中的 resource acquisition is initialization (RAII) 机制,此外,Rust 也不需要透过虚拟机器 (virtual machine) 来运行,其程序代码会转为原生的机械码。

    由于物理条件的限制,现在的 CPU 不再以冲高时脉为目标,而朝向多核心发展,能够支援共时性 (concurrency) 的程序语言,对于程序的效率,会有一定的提升。然而,比起一般的程序,共时性程序较难撰写,程序中也会有更多难以发现的臭虫 (bug)。Rust 同样将安全的特性放入共时性程序中,让程序设计者在安全的前提下,撰写共时运算的程序。

    系统程序语言 (system programming language) 指的是用来撰写系统软件 (system software) 的程序语言。系统软件和应用软件 (application software) 是相对的概念,应用软件直接和使用者互动,像是网页浏览器 (web browser) 或是文字处理器 (word processor) 等;系统软件则是和电脑硬件互动,或是提供执行应用软件的平台,像是操作系统、工具软件 (utility software)、驱动程序 (device driver)、编译器 (compiler) 等。近年来,系统程序语言的主流是 C 或 C++,而 Rust 提供相似的角色。然而,这不代表 Rust 不能用来写应用软件,就像是 C++ 也可以用来写应用软件一般。

    虽然 C/C++ 让我们不再需要为每个特定的平台撰写组合语言,然而,使用 C/C++ 仍然要去面对平台间的差异,所以才需要撰写条件式编译相关程序代码。撰写 Rust 程序代码时,不用再撰写条件式编译的程序代码,也就是说,Rust 程序代码是跨平台的。跨平台不是什么新闻,在 Java 之后的程序语言若不能跨平台很难有立足之地,然而,大部分的高阶程序语言都依赖某个特定的执行环境 (runtime environment),而 Rust 程序代码在跨平台的前提下,可产生原生机械码。

    另外,值得一提的是,Rust 和其他语言间的合作。虽然 Rust 的角色类似 C 和 C++,但是,许许多多以 C 和 C++ 撰写的函式库,已经相当成熟稳健,重新以 Rust 实现这些函式库不是明智的选择,而 Rust 提供接口让我们很容易地再利用这些函式库,减少重造轮子所浪费的心力。另一方面,某些任务,使用其他高阶语言即可完成,也不需要用 Rust 全部重写,这时候,Rust 的角色就如同 C 或 C++ 一般,为这些高阶语言提供延伸模块,在关键步骤为程序加速。

    由于 Rust 在演进的过程中,尝试了一些语法特性,但后来又废弃不用,使得网络上一些关于 Rust 的文章变成错误的资讯,学习者透过网络学习 Rust 时,需多方尝试。随着 1.0 版发布,Rust 的核心特性大抵上稳定下来,这个问题逐渐减少。

    由于 Rust 还是一个年轻的语言,目前的函式库 (library) 和框架 (framework) 没有那么丰富。使用者不能期待像 Java 或是 Python 般完整的社群资源。在将 Rust 引入自己的项目前,需要评估是否有充足的外部资源,还是需要自行实现。随着时间,Rust 社群成长,这一点应该会逐渐改善。

    至于其他的特性,在学习 Rust 的过程中就会逐渐体会,这里不再赘述。

    Rust vs. 其他语言

    程序语言间不存在绝对的优劣,这类讨论往往最后流于不同意识型态间的争执。此外,算法是独立于程序语言之外的,不会有某个算法只能用某个语言来实现的情形,即使某个语言缺乏某些特性,通常可以用其他的方法代替。语言的选择,往往是许多因素综合考量后的结果。这里的比较,基于笔者过去的经验,而带有主观的色彩,不是绝对的标准。读者可再多方收集相关资讯,但不需执着于谁好谁坏。

    vs. C++

    C++ 倾向于在语言层次提供各种丰富的机制,让使用者从中自由组合出期待的效果,这也使得 C++ 成为一个复杂的语言;Rust 也有丰富的语法机制,不过,Rust 吸收了许多函数式语言的特色,写起来和 C/C++ 风格的语言有所不同,需要一段时间来适应。C++ 将内存管理的责任留给使用者,Rust 则自动处理大部份的内存操作。比起 C++,Rust 的编译器较为严格,将许多常见的错误提前到编译时期,使得程序更为安全。

    vs. Java

    Java 从一开始就强调面向对象程序设计,将面向对象融入 Java 的语法;Rust 也支援面向对象,但并不特别强调。比起 Java,Rust 的面向对象略为不同,倾向用组合 (composition) 代替继承 (inheritance) 等。出于安全性的考量,Java 将指针操作取消,而 Rust 仍然保有指针操作。Java 程序需要在虚拟机器下运行,而 Rust 程序为原生机械码。

    vs. Python

    虽然 Rust 有着部分高阶语言的特性,比起 Python 这类高阶直译语言,仍需关注更多的细节,而需花费更多时间实现程序。和 Rust 相比,Python 支援更多高阶特性,使用者可以用更短的时间撰写程序。然而,纯 Python 实现的程序效能较差,随着程序规模增加,这个差距会更加明显。常见的开发模式是使用一些方式将程序加速,目前已有许多方案,像是 Cython 或 PyPy 等,Rust 可视为另一个新的作法。

    vs. Go

    Rust 和 Go 时常会拿来相比,由于 (1) 发布时间接近,(2) 同为可自动管理内存的编译语言,(3) 两者分别用不同的特性改善传统编译语言的缺点。Go 使用一套简单易学的语法机制,使用者可以很快熟悉大部分 Go 的特性,将其使用在自己的项目,但是,Go 缺乏部分重要的语法特性,为了向下兼容,短期内这些问题不会改变;Rust 的语法机制则较完整,但语法不稳定的问题,使得 Rust函数库相对不稳定。另外,Go 依赖垃圾回收,使得 Go 较不适合即时运算 (real-time computing) 方面的应用,而 Rust 没有这个问题。

    【赞助商连结】
    TAGS: INTRO, RUST