├── images ├── 0.png ├── editor-emacs-base.png ├── editor-emacs-jump.gif ├── editor-vim-wayslog.png ├── editor-vim-welldone.png ├── high-order-function.png ├── project-structure.png ├── function-return-value.png ├── editor-emacs-completion.png ├── get-mac-os-information.png ├── install-on-windows-1st.png ├── editor-visualstudio-racer.png ├── editor-emacs-error-checking.png ├── editor-visualstudio-add-files.png ├── editor-visualstudio-debugging.png ├── editor-visualstudio-download.png ├── editor-visualstudio-racersc.png ├── editor-visualstudio-settings.png ├── function-statement-expression.png ├── install-on-linux-check-system.png ├── install-on-linux-rust-success.png ├── editor-visualstudio-GDBproject.png ├── editor-visualstudio-debugging2.png ├── editor-visualstudio-newproject.png ├── editor-visualstudio-quickdebug.png ├── editor-visualstudio-setdebugger.png ├── editor-visualstudio-autocomplete.png ├── editor-visualstudio-set-breakpoints.png ├── editor-visualstudio-GDBproject-settings.png └── editor-visualstudio-GDBproject-settings2.png ├── src ├── example.rs └── emacs │ ├── 1 │ └── main.rs │ └── 2 │ └── main.rs ├── action ├── mysite │ ├── src │ │ ├── lib.rs │ │ └── main.rs │ └── Cargo.toml ├── preface.md ├── db │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── lib.rs └── json_data │ ├── Cargo.toml │ ├── src │ └── main.rs │ └── readme.md ├── editors ├── preface.md ├── vscode.md ├── atom.md ├── visualstudio.md ├── sublime.md ├── before.md ├── vim.md ├── emacs.md └── spacemacs.md ├── testing ├── preface.md └── bench.md ├── generic └── exercise │ ├── README.md │ ├── src │ ├── main.rs │ └── problem1.rs │ └── Cargo.toml ├── .textlintrc ├── cover ├── logo.png └── background.jpg ├── flow ├── preface.md ├── comment.md ├── condition.md └── repetition.md ├── type ├── preface.md ├── string.md └── types.md ├── unsafe-rawpointer ├── preface.md ├── raw-pointer.md └── unsafe.md ├── config.json ├── install ├── preface.md ├── install_rust_on_windows.md ├── install_rust_on_mac_os.md └── install_rust_on_linux.md ├── concurrency-parallel-thread ├── preface.md ├── parallel.md └── share-memory.md ├── attr-and-compiler-arg └── preface.md ├── rcarc ├── preface.md ├── mutex.md └── rcarc.md ├── .travis.yml ├── data-structure ├── preface.md ├── queue.md ├── linked_list.md ├── graph.md ├── priority_queue.md ├── binary_tree.md └── stack.md ├── book.json ├── intoborrow ├── preface.md ├── asref.md ├── borrow.md ├── deref.md ├── cow.md └── into.md ├── CONTRIBUTING.md ├── collections ├── overview.md ├── hashmap.md └── vec.md ├── match ├── overview.md ├── match.md └── pattern.md ├── iterator └── overview.md ├── .editorconfig ├── trait ├── overview.md └── trait-object.md ├── module ├── preface.md └── prelude.md ├── quickstart ├── quickstart.md ├── comments-document.md ├── vector-string.md ├── rust-travel.md ├── struct-enum.md ├── function-method.md ├── primitive-type.md ├── control-flow.md └── module-attribute.md ├── std ├── overview.md ├── net.md ├── fs-and-path.md └── process.md ├── ownership-system ├── preface.md └── borrowing_reference.md ├── function ├── overview.md ├── arguement.md ├── return_value.md ├── statement_expression.md └── higher_order_function.md ├── ffi ├── preface.md └── compiling-rust-to-lib.md ├── package.json ├── .gitignore ├── closure ├── overview.md ├── implementation.md └── syntax.md ├── io ├── output.md ├── preface.md ├── file-io.md └── io.md ├── README.md ├── appendix └── glossary.md ├── marker └── sendsync.md ├── safe └── safety.md ├── any └── any.md ├── 1st-glance └── README.md ├── operator-overloading └── operator.md └── heap-stack └── heap-stack.md /images/0.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/example.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /action/mysite/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn it_works() { 3 | } 4 | -------------------------------------------------------------------------------- /editors/preface.md: -------------------------------------------------------------------------------- 1 | # 编辑器 2 | 3 | 本章描述几种常用编辑器针对 Rust 开发环境的配置。 4 | -------------------------------------------------------------------------------- /testing/preface.md: -------------------------------------------------------------------------------- 1 | # 测试与评测 2 | 3 | 本章讲解 Rust 中内建的测试与评测相关知识。 4 | -------------------------------------------------------------------------------- /generic/exercise/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ======= 3 | 4 | `cargo run` 5 | -------------------------------------------------------------------------------- /.textlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "editorconfig": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /cover/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/cover/logo.png -------------------------------------------------------------------------------- /flow/preface.md: -------------------------------------------------------------------------------- 1 | # 基本程序结构 2 | 3 | Rust 是多范式语言,当然支持命令式编程风格。本章讲解 Rust 中的几种基本程序结构。 4 | -------------------------------------------------------------------------------- /type/preface.md: -------------------------------------------------------------------------------- 1 | # 类型、运算符和字符串 2 | 3 | 本章讲解 Rust 中的类型相关基础知识、运算符相关知识、和字符串的基本知识。 4 | -------------------------------------------------------------------------------- /unsafe-rawpointer/preface.md: -------------------------------------------------------------------------------- 1 | # Unsafe、原始指针 2 | 3 | 本章开始讲解 Rust 中的 `Unsafe` 部分。 4 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rust 中文教程", 3 | "introduction": "适合初学者的Rust中文教程" 4 | } -------------------------------------------------------------------------------- /install/preface.md: -------------------------------------------------------------------------------- 1 | # 安装Rust 2 | 3 | 本章讲解在三大平台 Linux, MacOS, Windows 上分别安装 Rust 的步骤。 4 | -------------------------------------------------------------------------------- /cover/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/cover/background.jpg -------------------------------------------------------------------------------- /concurrency-parallel-thread/preface.md: -------------------------------------------------------------------------------- 1 | # 并发,并行,多线程编程 2 | 3 | 本章讲解 Rust 中,并发,并行,多线程编程的相关知识。 4 | 5 | -------------------------------------------------------------------------------- /attr-and-compiler-arg/preface.md: -------------------------------------------------------------------------------- 1 | # 属性和编译器参数 2 | 3 | 本章将介绍Rust语言中的属性(Attribute)和编译器参数(Compiler Options)。 4 | -------------------------------------------------------------------------------- /rcarc/preface.md: -------------------------------------------------------------------------------- 1 | # 几种智能指针 2 | 3 | 本章讲解 `Rc`, `Arc`, `Mutex`, `RwLock`, `Cell`, `RefCell` 的知识和使用方法。 4 | 5 | -------------------------------------------------------------------------------- /src/emacs/2/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut x = vec!["Hello", "world"]; 3 | 4 | let y = &x[0]; 5 | } -------------------------------------------------------------------------------- /images/editor-emacs-base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-emacs-base.png -------------------------------------------------------------------------------- /images/editor-emacs-jump.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-emacs-jump.gif -------------------------------------------------------------------------------- /images/editor-vim-wayslog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-vim-wayslog.png -------------------------------------------------------------------------------- /images/editor-vim-welldone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-vim-welldone.png -------------------------------------------------------------------------------- /images/high-order-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/high-order-function.png -------------------------------------------------------------------------------- /images/project-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/project-structure.png -------------------------------------------------------------------------------- /images/function-return-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/function-return-value.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "5" 5 | 6 | script: 7 | - npm test 8 | - npm run build 9 | -------------------------------------------------------------------------------- /images/editor-emacs-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-emacs-completion.png -------------------------------------------------------------------------------- /images/get-mac-os-information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/get-mac-os-information.png -------------------------------------------------------------------------------- /images/install-on-windows-1st.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/install-on-windows-1st.png -------------------------------------------------------------------------------- /data-structure/preface.md: -------------------------------------------------------------------------------- 1 | # 常用数据结构实现 2 | 3 | 本章讲解如何使用 Rust 进行一些常用数据结构的实现。实现的代码仅作示例用,并不一定十分高效。真正使用的时候,请使用标准库或第三方成熟库中的数据结构。 4 | -------------------------------------------------------------------------------- /images/editor-visualstudio-racer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-racer.png -------------------------------------------------------------------------------- /images/editor-emacs-error-checking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-emacs-error-checking.png -------------------------------------------------------------------------------- /images/editor-visualstudio-add-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-add-files.png -------------------------------------------------------------------------------- /images/editor-visualstudio-debugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-debugging.png -------------------------------------------------------------------------------- /images/editor-visualstudio-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-download.png -------------------------------------------------------------------------------- /images/editor-visualstudio-racersc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-racersc.png -------------------------------------------------------------------------------- /images/editor-visualstudio-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-settings.png -------------------------------------------------------------------------------- /images/function-statement-expression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/function-statement-expression.png -------------------------------------------------------------------------------- /images/install-on-linux-check-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/install-on-linux-check-system.png -------------------------------------------------------------------------------- /images/install-on-linux-rust-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/install-on-linux-rust-success.png -------------------------------------------------------------------------------- /action/preface.md: -------------------------------------------------------------------------------- 1 | # 实战篇 2 | 3 | 本章举 3 个实际中的例子,来小小展示一下 Rust 在实际中的应用。它们分别是: 4 | 5 | - Json处理 6 | - Web 应用开发入门 7 | - 使用Postgresql数据库 8 | -------------------------------------------------------------------------------- /images/editor-visualstudio-GDBproject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-GDBproject.png -------------------------------------------------------------------------------- /images/editor-visualstudio-debugging2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-debugging2.png -------------------------------------------------------------------------------- /images/editor-visualstudio-newproject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-newproject.png -------------------------------------------------------------------------------- /images/editor-visualstudio-quickdebug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-quickdebug.png -------------------------------------------------------------------------------- /images/editor-visualstudio-setdebugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-setdebugger.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang":"zh-cn", 3 | "pluginsConfig": { 4 | "theme-default": { 5 | "showLevel": true 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /generic/exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | mod problem1; 2 | 3 | fn main() { 4 | problem1::demo("Cargo.toml"); 5 | problem1::demo(""); 6 | } 7 | -------------------------------------------------------------------------------- /images/editor-visualstudio-autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-autocomplete.png -------------------------------------------------------------------------------- /action/db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "db" 3 | version = "0.1.0" 4 | authors = ["xxx "] 5 | 6 | [dependencies] 7 | postgres="*" -------------------------------------------------------------------------------- /images/editor-visualstudio-set-breakpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-set-breakpoints.png -------------------------------------------------------------------------------- /images/editor-visualstudio-GDBproject-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-GDBproject-settings.png -------------------------------------------------------------------------------- /images/editor-visualstudio-GDBproject-settings2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/RustPrimer/HEAD/images/editor-visualstudio-GDBproject-settings2.png -------------------------------------------------------------------------------- /action/mysite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mysite" 3 | version = "0.1.0" 4 | authors = ["wangyu <190810401@qq.com>"] 5 | 6 | [dependencies] 7 | iron = "*" 8 | -------------------------------------------------------------------------------- /generic/exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exercise" 3 | version = "0.1.0" 4 | authors = ["Knight "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /intoborrow/preface.md: -------------------------------------------------------------------------------- 1 | # 类型系统中的几个常见 trait 2 | 3 | 本章讲解 Rust 类型系统中的几个常见 trait。有 `Into, From, AsRef, AsMut, Borrow, BorrowMut, ToOwned, Deref, Cow`。 4 | 5 | 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 我们欢迎RustPrimer的Contributor们将自己的 `[blog/github/社交帐号]` 2 | 添加在 [1st-glance/README.md](./1st-glance/README.md)里。 3 | 但严禁未参与者恶意添加帐号,违者将会被永久拒绝PR和issue权限。 4 | -------------------------------------------------------------------------------- /action/json_data/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "json_data" 3 | version = "0.1.0" 4 | authors = ["wangyu <190810401@qq.com>"] 5 | 6 | [dependencies] 7 | rustc-serialize = "0.3.18" 8 | -------------------------------------------------------------------------------- /collections/overview.md: -------------------------------------------------------------------------------- 1 | # 集合类型 2 | 就像C++的stl一样,Rust提供了一系列的基础且通用的容器类型。善用这些集合类型,可以让Rust编程更加方便轻松,但每种数据结构都会有其局限性,合理的选型方能维持更好的效率。 3 | 4 | 本章目录: 5 | 6 | * [Vec](vec.md) 7 | * [HashMap](hashmap.md) 8 | -------------------------------------------------------------------------------- /match/overview.md: -------------------------------------------------------------------------------- 1 | # 模式匹配 2 | 3 | 除了我们常见的控制语句之外,Rust还提供了一个更加强大的关键字——`match` 。但是,需要指出的一点是,match只是*匹配*,要发挥其全部威力,还需要*模式*的配合。本章,我们就将的对Rust的模式匹配进行一番探索。 4 | 5 | 本章内容: 6 | * [match关键字](match.md) 7 | * [模式](pattern.md) 8 | -------------------------------------------------------------------------------- /iterator/overview.md: -------------------------------------------------------------------------------- 1 | # 迭代器 2 | 3 | 在Rust中,迭代器共分为三个部分:迭代器、适配器、消费者。 4 | 5 | 其中,迭代器本身提供了一个惰性的序列,适配器对这个序列进行诸如筛选、拼接、转换查找等操作,消费者则在前两者的基础上生成最后的数值集合。 6 | 7 | 但是,孤立的看这三者其实是没有意义的,因此,本章将在一个大节里联系写出三者。 8 | 9 | [迭代器](iterator.md) 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{md,markdown}] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /trait/overview.md: -------------------------------------------------------------------------------- 1 | # trait 和 trait对象 2 | 3 | **trait**(特征)类似于其他语言中的**interface**或者**protocol**,指定一个实际类型必须满足的功能集合 4 | 与interface不同的地方在于,interface会隐藏具体实现类型,而trait不会。在rust中,隐藏实现类型可以由generic配合trait作出。 5 | 6 | **Rust**中的trait: 7 | 8 | * [trait关键字](trait.md) 9 | * [trait对象](trait-object.md) 10 | -------------------------------------------------------------------------------- /src/emacs/1/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync:Mutex; 3 | 4 | fn main() { 5 | let mut data = Mutex::new(vec![1u32, 2, 3]); 6 | 7 | for i in 0..3 { 8 | let data = data.lock().unwrap(); 9 | thread::spawn(move || { 10 | data[i] += 1; 11 | }); 12 | } 13 | 14 | thread::sleep_ms(50); 15 | } -------------------------------------------------------------------------------- /module/preface.md: -------------------------------------------------------------------------------- 1 | # 模块和包系统、Prelude 2 | 3 | ## 前言 4 | 5 | 随着工程的增大,把所有代码写在一个文件里面,是一件极其初等及愚蠢的作法。大体来讲,它有如下几个缺点: 6 | 7 | 1. 文件大了,编辑器打开慢; 8 | 2. 所有代码放在同一个文件中,无法很好地利用现代多窗口编辑器,同时查看编辑相关联的两个代码片断; 9 | 3. 代码数量过多,查找某一个关键词过慢,定位到某一行代码的效率会大大降低; 10 | 4. 会大大增加上翻下翻的频率,导致你的鼠标中间滚轮易坏; 11 | 5. 不断地上翻下翻,会导致你头晕; 12 | 6. 头晕了,就容易写出错误的代码,甚至改错文件中的某一行(相似的地方,改错地方了); 13 | 7. 出现bug,根据错误反馈,知道是哪一片逻辑的问题,但不容易快速定位; 14 | 15 | 因此,模块是几乎所有语言的基础设施,尽管叫法各有不同。 16 | -------------------------------------------------------------------------------- /quickstart/quickstart.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | 本章的目的在于快速上手(Quickstart),对Rust语言建立初步的印象。 4 | 前面的章节中,我们已经安装好了Rust,配置好了编辑器,相信你一定已经跃跃欲试了。 5 | 注意: 本章的一些概念只需要大概了解就行,后续的章节将会有详细的讲解,但是本章的例子请务必亲自手敲并运行一遍。 6 | 7 | 下面,让我们开始动手写Rust程序吧! 8 | 9 | ps:本章原始章节由 ee0703 书写的。因为内容不太满意,由 [Naupio(N猫)](https://github.com/Naupio)重写了整个章节,并加入大量的内容。特别鸣谢 [photino](https://github.com/photino) 提供的 [rust-notes](https://github.com/photino/rust-notes) 。本章也有大量内容编辑自 [Naupio(N猫)](https://github.com/Naupio) 创作中的 Rust 新书的快速入门章节。 10 | -------------------------------------------------------------------------------- /std/overview.md: -------------------------------------------------------------------------------- 1 | # 标准库示例 2 | 3 | 好了,本书到这里也接近完结了。相信你一在学习了这么多内容的之后,一定跃跃欲试了吧? 4 | 下面,我们将以代码为主,讲解几个利用`std`库,即标准库来做的例子。希望大家能从中学到一点写法,并开始自己的Rust之旅。 5 | 6 | * 注: 由于笔者的电脑是openSUSE Linux的,所以本章所有代码均只在 `openSUSE Leap 42.1 && rustc 1.9.0-nightly (52e0bda64 2016-03-05)` 下编译通过,对Linux适配可能会更好一点,其他系统的同学请自行参照。 7 | 8 | 另:本章原本设计的时候附加有时间api的处理,但是在本章写作的时候Rust的大部分时间API还处于Unstable状态,随时可能遭到删除或重写。因此,我们暂时删除了时间API的操作。等以后Rust的API稳定之后,再来补齐这一节。 9 | 10 | 1. [系统命令:调用grep](process.md) 11 | 2. [目录操作:简单grep](fs-and-path.md) 12 | 3. [网络模块:W回音](net.md) 13 | -------------------------------------------------------------------------------- /ownership-system/preface.md: -------------------------------------------------------------------------------- 1 | # 所有权系统 2 | 3 | ## 概述 4 | 5 | 所有权系统(Ownership System)是Rust语言最基本最独特也是最重要的特性之一。 6 | 7 | Rust追求的目标是内存安全与运行效率,但是它却没有golang, java, python等语言的内存垃圾回收机制GC。 8 | 9 | Rust语言号称,只要编译通过就不会崩溃(内存安全);拥有着零或者极小的运行时开销(运行效率)。这些优点也都得益于Rust的所有权系统。 10 | 11 | 所有权系统,包括三个重要的组成部分: 12 | 13 | - **Ownership**(所有权) 14 | - **Borrowing**(借用) 15 | - **Lifetimes**(生命周期) 16 | 17 | 这三个特性之间相互关联,后面章节会依次全面讲解。 18 | 19 | > **提示:** 20 | > Rust的所有权系统对很多初学者来说,可能会觉得难以理解,Rust的内存检查是在编译阶段完成,这个检查是非常严谨的,所以初学者在编译代码的时候,刚开始可能很难一次编译通过。 21 | 22 | > 不过不要害怕:),当你一旦了解熟悉它后你会喜欢上它,并且在日后的编程中受益颇多。所有权系统需要读者慢慢体会其中的奥秘,学习过程中也可以参考官方文档。 23 | -------------------------------------------------------------------------------- /editors/vscode.md: -------------------------------------------------------------------------------- 1 | # VS Code 安装配置 2 | 3 | [VS Code](https://code.visualstudio.com/) 是微软出的一款开源代码编辑器,秉承了微软在IDE领域的一惯优秀基因,是一款潜力相当大的编辑器/IDE。 4 | 5 | VScode 目前也对 Rust 也有良好的支持。 6 | 7 | 8 | 9 | ## 下载 VScode 10 | 11 | 请打开官网 https://code.visualstudio.com/ 下载编辑器。 12 | 13 | ## 依赖 14 | 15 | 如本章第一节所述,准备好 `racer`,`rust 源代码`,`rustfmt`,`rls` 这四样东西,并且配置好相应的环境变量,此不赘述。 16 | 17 | ## 安装 Rust 扩展 Rust 18 | 19 | 1. 打开 VScode 编辑器; 20 | 2. 按 Ctrl + p 打开命令面板; 21 | 3. 在编辑器中上部浮现出的输入框中,输入 `ext install vscode-rust`,会自动搜索可用的插件,搜索出来后,点击进行安装; 22 | 4. 使用`VScode`打开任意一个`.rs`文件,插件首次启动会自动引导用户完成配置。 23 | 24 | 注:推荐使用RLS模式,即使用[Rust Langular Server](https://github.com/rust-lang-nursery/rls)提供各项功能支持 25 | -------------------------------------------------------------------------------- /function/overview.md: -------------------------------------------------------------------------------- 1 | # 函数 2 | 尽管rust是一门多范式的编程语言,但rust的编程风格是更偏向于函数式的,函数在rust中是“一等公民”——first-class type。这意味着,函数是可以作为数据在程序中进行传递,如:作为函数的参数。跟C、C++一样,rust程序也有一个唯一的程序入口-main函数。rust的main函数形式如下: 3 | 4 | ```rust 5 | fn main() { 6 | //statements 7 | } 8 | ``` 9 | 10 | rust使用 `fn` 关键字来声明和定义函数,`fn` 关键字隔一个空格后跟函数名,函数名后跟着一个括号,函数参数定义在括号内。rust使用`snake_case`风格来命名函数,即所有字母小写并使用下划线类分隔单词,如:`foo_bar`。如果函数有返回值,则在括号后面加上箭头 __->__ ,在箭头后加上返回值的类型。 11 | 12 | 这一章我们将学习以下与函数相关的知识: 13 | 1. [函数参数](arguement.md) 14 | 2. [函数返回值](return_value.md) 15 | 3. [语句和表达式](statement_expression.md) 16 | 4. [高阶函数](higher_order_function.md) 17 | 18 | > ### 注:本章所有例子均在rustc1.4下编译通过,且例子中说明的所有的编译错误都是rustc1.4版本给出的。 19 | -------------------------------------------------------------------------------- /ffi/preface.md: -------------------------------------------------------------------------------- 1 | # FFI 2 | 3 | 4 | FFI([Foreign Function Interface](https://en.wikipedia.org/wiki/Foreign_function_interface))是用来与其它语言交互的接口,在有些语言里面称为语言绑定(language bindings),Java 里面一般称为 JNI(Java Native Interface) 或 JNA(Java Native Access)。由于现实中很多程序是由不同编程语言写的,必然会涉及到跨语言调用,比如 A 语言写的函数如果想在 B 语言里面调用,这时一般有两种解决方案:一种是将函数做成一个服务,通过进程间通信([IPC](https://en.wikipedia.org/wiki/Inter-process_communication))或网络协议通信([RPC](https://en.wikipedia.org/wiki/Remote_procedure_call), [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer)等);另一种就是直接通过 FFI 调用。前者需要至少两个独立的进程才能实现,而后者直接将其它语言的接口内嵌到本语言中,所以调用效率比前者高。 5 | 6 | 当前的系统编程领域大部分被 C/C++ 占领,而 Rust 定位为系统编程语言,少不了与现有的 C/C++ 代码交互,另外为了给那些"慢"脚本语言调用,Rust 必然得对 FFI 有完善的支持,本章我们就来谈谈 Rust 的 FFI 系统。 7 | -------------------------------------------------------------------------------- /module/prelude.md: -------------------------------------------------------------------------------- 1 | 2 | # Prelude 3 | 4 | Rust 的标准库,有一个 `prelude` 子模块,这里面包含了默认导入(std 库是默认导入的,然后 std 库中的 prelude 下面的东西也是默认导入的)的所有符号。 5 | 6 | 大体上有下面一些内容: 7 | 8 | ```rust 9 | std::marker::{Copy, Send, Sized, Sync} 10 | std::ops::{Drop, Fn, FnMut, FnOnce} 11 | std::mem::drop 12 | std::boxed::Box 13 | std::borrow::ToOwned 14 | std::clone::Clone 15 | std::cmp::{PartialEq, PartialOrd, Eq, Ord} 16 | std::convert::{AsRef, AsMut, Into, From} 17 | std::default::Default 18 | std::iter::{Iterator, Extend, IntoIterator, DoubleEndedIterator, ExactSizeIterator} 19 | std::option::Option::{self, Some, None} 20 | std::result::Result::{self, Ok, Err} 21 | std::slice::SliceConcatExt 22 | std::string::{String, ToString} 23 | std::vec::Vec 24 | ``` 25 | -------------------------------------------------------------------------------- /action/db/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate postgres; 2 | extern crate db; 3 | 4 | use postgres::{Connection, SslMode}; 5 | 6 | use db::*; 7 | 8 | struct Blog { 9 | title: String, 10 | body: String, 11 | } 12 | 13 | fn main() { 14 | let conn:Connection=connect(); 15 | 16 | let blog = Blog{ 17 | title: String::from("title"), 18 | body: String::from("body"), 19 | }; 20 | let title = blog.title.to_string(); 21 | let body = blog.body.to_string(); 22 | insert_info(&conn,&title,&body); 23 | 24 | for row in query::(&conn,"select * from blog"){ 25 | println!("{:?}",row); 26 | } 27 | let sql = "select * from person"; 28 | query_all(&conn,&sql); 29 | } 30 | ~ -------------------------------------------------------------------------------- /generic/exercise/src/problem1.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::fs::File; 3 | use std::io::{BufRead, BufReader}; 4 | 5 | fn parse(lines: I) 6 | where I: IntoIterator, I::Item: Borrow 7 | { 8 | for line in lines { 9 | println!("{}", line.borrow()); 10 | } 11 | } 12 | 13 | pub fn demo(path: &str) { 14 | if path.is_empty() { 15 | println!("==== Parsing long string ===="); 16 | let content = "some\nlong\ntext"; 17 | parse(content.lines()); 18 | } else { 19 | println!("==== Parsing text file ===="); 20 | let f = File::open(path).unwrap(); 21 | let b = BufReader::new(f); 22 | parse(b.lines().map(|l| l.unwrap_or("".to_owned()))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-primer", 3 | "version": "1.1.2", 4 | "description": "The Rust primer for beginners.", 5 | "scripts": { 6 | "test": "textlint $(ls --ignore='node_modules')", 7 | "test:fix": "npm test -- --fix", 8 | "serve": "gitbook serve", 9 | "build": "gitbook build" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rustcc/RustPrimer.git" 14 | }, 15 | "keywords": [ 16 | "Rust", 17 | "Book", 18 | "Beginner" 19 | ], 20 | "license": "CC-BY-SA-3.0", 21 | "bugs": { 22 | "url": "https://github.com/rustcc/RustPrimer/issues" 23 | }, 24 | "homepage": "https://github.com/rustcc/RustPrimer#readme", 25 | "devDependencies": { 26 | "gitbook-cli": "^2.1.3", 27 | "textlint": "6.5.0", 28 | "textlint-rule-editorconfig": "1.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /action/json_data/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_serialize; 2 | use rustc_serialize::json; 3 | 4 | // Automatically generate `RustcDecodable` and `RustcEncodable` trait 5 | // implementations 6 | #[derive(RustcDecodable, RustcEncodable)] 7 | pub struct TestStruct { 8 | data_int: u8, 9 | data_str: String, 10 | data_vector: Vec, 11 | } 12 | 13 | fn main() { 14 | let object = TestStruct { 15 | data_int: 1, 16 | data_str: "homura".to_string(), 17 | data_vector: vec![2,3,4,5], 18 | }; 19 | 20 | // Serialize using `json::encode` 21 | let encoded = json::encode(&object).unwrap(); 22 | //println!("{:?}",encoded); 23 | println!("{}",encoded); 24 | // Deserialize using `json::decode` 25 | let decoded: TestStruct = json::decode(&encoded).unwrap(); 26 | println!("{:?}",decoded.data_vector); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built files 2 | _book/ 3 | 4 | # Node-related ignoring, copied from https://github.com/github/gitignore/blob/master/Node.gitignore 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules 35 | jspm_packages 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | -------------------------------------------------------------------------------- /install/install_rust_on_windows.md: -------------------------------------------------------------------------------- 1 | # Rust for Windows 2 | 3 | Rust 支持主流的操作系统,Linux,Mac和 Windows。 4 | 5 | Rust在Windows上的安装和你在windows上安装其它软件一样。 6 | 7 | ### 1、下载安装包: 8 | 9 | [下载地址](https://www.rust-lang.org/zh-CN/other-installers.html) 10 | 11 | Rust提供了多个版本和多个平台的安装包,下载对应的即可,此处我们以[1.6.0](https://static.rust-lang.org/dist/rust-1.6.0-x86_64-pc-windows-gnu.msi)的稳定版为例。 12 | 13 | ### 2、安装: 14 | 双击下载到的安装包,如下图所示: 15 | 16 | ![Mac-os-inofrmatoin](../images/install-on-windows-1st.png) 17 | 18 | 默认,rust将安装到所有用户下,选择“Advanced”,可以指定安装用户和安装路径。然后点击"install"等待几分钟即可(中间可能会有安全提示,点击允许即可,如果你装了360之类的,需要小心360阻止写入注册表)。 19 | 20 | ### 3.验证安装: 21 | 22 | 安装完成后,运行windows命令行,然后输入: 23 | 24 | `rustc --version` 25 | 26 | 看到 以 **rustc 1.6.0** 开头,说明你安装成功了。 27 | 28 | **注意** 29 | 30 | 中国科学技术大学镜像源包含 [rust-static](http://mirrors.ustc.edu.cn/rust-static/),梯子暂时出问题的同学可以尝试从这里下载编译器;除此之外。还有 Crates 源,详见[这里的说明](https://servers.ustclug.org/2016/01/mirrors-add-rust-crates/)。 31 | -------------------------------------------------------------------------------- /closure/overview.md: -------------------------------------------------------------------------------- 1 | # 闭包 2 | 闭包是什么?先来看看[维基百科][wiki]上的描述: 3 | 4 | >在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是 __引用了自由变量的函数__。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

5 | 闭包的概念出现于60年代,最早实现闭包的程序语言是Scheme。之后,闭包被广泛使用于函数式编程语言如ML语言和LISP。很多命令式程序语言也开始支持闭包。 6 | 7 | [wiki]:https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6) 8 | 9 | 可以看到,第一句就已经说明了什么是闭包:闭包是引用了自由变量的函数。所以,闭包是一种特殊的函数。 10 | 11 | 在rust中,函数和闭包都是实现了`Fn`、`FnMut`或`FnOnce`特质(trait)的类型。任何实现了这三种特质其中一种的类型的对象,都是 __可调用对象__ ,都能像函数和闭包一样通过这样`name()`的形式调用,`()`在rust中是一个操作符,操作符在rust中是可以重载的。rust的操作符重载是通过实现相应的`trait`来实现,而`()`操作符的相应`trait`就是`Fn`、`FnMut`和`FnOnce`,所以,任何实现了这三个`trait`中的一种的类型,其实就是重载了`()`操作符。关于`Fn`、`FnMut`和`FnOnce`的说明请看第二节闭包的实现。 12 | 13 | 本章主要分四节讲述: 14 | 15 | * [第一节 概要](overview.md) 16 | * [第二节 闭包的语法](syntax.md) 17 | * [第三节 闭包的实现](implementation.md) 18 | * [第四节 闭包作为参数或返回值](as_argument_return_value.md) 19 | -------------------------------------------------------------------------------- /quickstart/comments-document.md: -------------------------------------------------------------------------------- 1 | # 注释与文档 2 | 3 | ## 注释 4 | 在 Rust 里面注释分成两种,行注释和块注释。它的形式和 C 语言是一样的。 5 | 两种注释分别是: 6 | > 1. 行注释使用 `//` 放在注释前面。比如: 7 | 8 | ``` 9 | // I love Rust, but I hate Rustc. 10 | ``` 11 | 12 | > 2. 块注释分别使用`/*`和`*/`包裹需要注释的内容。比如: 13 | 14 | ``` 15 | /* W-Cat 是个大胖猫,N-Cat 是个高度近视猫。*/ 16 | ``` 17 | 18 | ## 文档 19 | Rust 自带有文档功能的注释,分别是`///`和`//!`。支持 Markdown 格式 20 | 1. `///`用来描述的它后面接着的项。 21 | 2. `//!`用来描述包含它的项,一般用在模块文件的头部。 22 | 比如在 main.rs 文件中输入以下内容: 23 | 24 | ``` 25 | //! # The first line 26 | //! The second line 27 | /// Adds one to the number given. 28 | /// 29 | /// # Examples 30 | /// 31 | /// ``` 32 | /// let five = 5; 33 | /// 34 | /// assert_eq!(6, add_one(5)); 35 | /// # fn add_one(x: i32) -> i32 { 36 | /// # x + 1 37 | /// # } 38 | /// ``` 39 | fn add_one(x: i32) -> i32 { 40 | x + 1 41 | } 42 | ``` 43 | 44 | ### 生成 html 文档 45 | * `rustdoc main.rs` 46 | 47 | 或者 48 | 49 | * `cargo doc` 50 | -------------------------------------------------------------------------------- /intoborrow/asref.md: -------------------------------------------------------------------------------- 1 | # AsRef 和 AsMut 2 | 3 | `std::convert` 下面,还有另外两个 Trait,`AsRef/AsMut`,它们功能是配合泛型,在执行引用操作的时候,进行自动类型转换。这能够使一些场景的代码实现得清晰漂亮,大家方便开发。 4 | 5 | ## AsRef 6 | 7 | `AsRef` 提供了一个方法 `.as_ref()`。 8 | 9 | 对于一个类型为 `T` 的对象 `foo`,如果 `T` 实现了 `AsRef`,那么,`foo` 可执行 `.as_ref()` 操作,即 `foo.as_ref()`。操作的结果,我们得到了一个类型为 `&U` 的新引用。 10 | 11 | 注: 12 | 13 | 1. 与 `Into` 不同的是,`AsRef` 只是类型转换,`foo` 对象本身没有被消耗; 14 | 2. `T: AsRef` 中的 `T`,可以接受 资源拥有者(owned)类型,共享引用(shared referrence)类型 ,可变引用(mutable referrence)类型。 15 | 16 | 下面举个简单的例子: 17 | 18 | ```rust 19 | fn is_hello>(s: T) { 20 | assert_eq!("hello", s.as_ref()); 21 | } 22 | 23 | let s = "hello"; 24 | is_hello(s); 25 | 26 | let s = "hello".to_string(); 27 | is_hello(s); 28 | ``` 29 | 30 | 因为 `String` 和 `&str` 都实现了 `AsRef`。 31 | 32 | 33 | ## AsMut 34 | 35 | `AsMut` 提供了一个方法 `.as_mut()`。它是 `AsRef` 的可变(mutable)引用版本。 36 | 37 | 对于一个类型为 `T` 的对象 `foo`,如果 `T` 实现了 `AsMut`,那么,`foo` 可执行 `.as_mut()` 操作,即 `foo.as_mut()`。操作的结果,我们得到了一个类型为 `&mut U` 的可变(mutable)引用。 38 | 39 | 注:在转换的过程中,`foo` 会被可变(mutable)借用。 40 | -------------------------------------------------------------------------------- /io/output.md: -------------------------------------------------------------------------------- 1 | # print! 宏 2 | 3 | 我们在快速入门中就提到过标准输出的行缓冲。它一个表现就是 `print!` 宏。如果你在 `print!` 宏后面接上一个输入就会发现这种按行缓冲的机制。 4 | 5 | ```rust 6 | fn main() { 7 | print!("hello!\ninput:"); 8 | let mut input = String::new(); 9 | std::io::stdin() 10 | .read_line(&mut input) 11 | .expect("Failed to read line"); 12 | println!("line:{}",input); 13 | } 14 | ``` 15 | 16 | 您可以编译并运行这段程序试一试,您会发现我们并没有得到预期的(下划线代表光标的位置): 17 | 18 | ``` 19 | hello! 20 | input:_ 21 | ``` 22 | 23 | 而是得到了: 24 | 25 | ``` 26 | hello! 27 | _ 28 | ``` 29 | 30 | 这就是由于标准输出中的这种行缓冲机制,在遇到换行符之前,输出的内容并不会隐式的刷新,这就导致 `print!` 宏和 `println!` 宏实际上并不完全相同。在标准库中 `print!` 宏是这样的: 31 | 32 | ```rust 33 | macro_rules! print { 34 | ($($arg:tt)*) => { ... }; 35 | } 36 | ``` 37 | 38 | 由此,我们可以对它进行改进,使它和 `println!` 宏被自动刷新,不过这种刷新是一种显式的刷新。 39 | 40 | ```rust 41 | use std::io::{self, Write}; 42 | 43 | macro_rules! printf { 44 | ($($arg:tt)*) =>{ 45 | print!($($arg)*); 46 | io::stdout().flush().unwrap(); 47 | } 48 | } 49 | ``` 50 | 51 | 此外,当您需要刷新还没有遇到换行符的一行内容的时候您都可以使用 `io::stdout().flush().unwrap();` 进行刷新,不过需要注意的是要先 `use std::io::{self, Write};` 如果您不这样做,将会得到一个错误。 52 | -------------------------------------------------------------------------------- /data-structure/queue.md: -------------------------------------------------------------------------------- 1 | # 队列 2 | 3 | ## 队列简介 4 | 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。 5 | 6 | >在队列的形成过程中,可以利用线性链表的原理,来生成一个队列。基于链表的队列,要动态创建和删除节点,效率较低,但是可以动态增长。队列采用的 **FIFO(first in first out)**,新元素(等待进入队列的元素)总是被插入到链表的尾部,而读取的时候总是从链表的头部开始读取。每次读取一个元素,释放一个元素。所谓的动态创建,动态释放。因而也不存在溢出等问题。由于链表由结构体间接而成,遍历也方便。 7 | 8 | ## 队列实现 9 | 下面看一下我们使用 Vec 来实现的简单 Queue: 10 | 11 | 主要实现的`new( ), push( ), pop( )`三个方法 12 | 13 | ```rust 14 | #[derive(Debug)] 15 | struct Queue { 16 | qdata: Vec, 17 | } 18 | 19 | impl Queue { 20 | fn new() -> Self { 21 | Queue{qdata: Vec::new()} 22 | } 23 | 24 | fn push(&mut self, item:T) { 25 | self.qdata.push(item); 26 | } 27 | 28 | fn pop(&mut self) -> T{ 29 | self.qdata.remove(0) 30 | } 31 | } 32 | 33 | fn main() { 34 | let mut q = Queue::new(); 35 | q.push(1); 36 | q.push(2); 37 | println!("{:?}", q); 38 | q.pop(); 39 | println!("{:?}", q); 40 | q.pop(); 41 | } 42 | ``` 43 | 44 | ## 练习 45 | 看起来比我们在上一节实现的Stack简单多了。不过这个Queue实现是有Bug的。 46 | 47 | 练习:在这个代码的上找到 Bug,并修改。 48 | 49 | 提示:`pop( )`方法有 Bug,请参考 Stack 小节的实现,利用 Option 来处理。 50 | -------------------------------------------------------------------------------- /closure/implementation.md: -------------------------------------------------------------------------------- 1 | # 闭包的实现 2 | 3 | Rust 的闭包实现与其它语言有些许不同。它们实际上是trait的语法糖。在这以前你会希望阅读[trait章节](https://doc.rust-lang.org/stable/book/traits.html),和[trait对象](https://doc.rust-lang.org/stable/book/trait-objects.html)。 4 | 5 | 都理解吗?很好。 6 | 7 | 理解闭包底层是如何工作的关键有点奇怪:使用`()`调用函数,像`foo()`,是一个可重载的运算符。到此,其它的一切都会明了。在Rust中,我们使用trait系统来重载运算符。调用函数也不例外。我们有三个trait来分别重载: 8 | 9 | ```rust 10 | # mod foo { 11 | pub trait Fn : FnMut { 12 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; 13 | } 14 | 15 | pub trait FnMut : FnOnce { 16 | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; 17 | } 18 | 19 | pub trait FnOnce { 20 | type Output; 21 | 22 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; 23 | } 24 | # } 25 | ``` 26 | 27 | 你会注意到这些 trait 之间的些许区别,不过一个大的区别是`self`:`Fn`获取`&self`,`FnMut`获取`&mut self`,而`FnOnce`获取`self`。这包含了所有3种通过通常函数调用语法的`self`。不过我们将它们分在 3 个 trait 里,而不是单独的 1 个。这给了我们大量的对于我们可以使用哪种闭包的控制。 28 | 29 | 闭包的`|| {}`语法是上面 3 个 trait 的语法糖。Rust 将会为了环境创建一个结构体,`impl`合适的 trait,并使用它。 30 | 31 | > ### 这部分引用自[The Rust Programming Language中文版](https://github.com/KaiserY/rust-book-chinese/blob/master/content/Closures%20%E9%97%AD%E5%8C%85.md) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RustPrimer 2 | 3 | [![build status][build-badge]][build-url] 4 | 5 | [build-badge]: https://api.travis-ci.org/rustcc/RustPrimer.svg 6 | [build-url]: https://travis-ci.org/rustcc/RustPrimer 7 | 8 | The Rust primer for beginners. 9 | 10 | 给初学者的Rust中文教程。 11 | 12 | ## 在线阅读地址 13 | 14 | [点我阅读](https://rustcc.gitbooks.io/rustprimer/content/) 15 | 16 | 也可复制以下链接: 17 | 18 | ``` 19 | https://rustcc.gitbooks.io/rustprimer/content/ 20 | ``` 21 | 22 | ## 社区 23 | 24 | ### QQ群 25 | 26 | 群号: 27 | ``` 28 | Rust语言中文社区 144605258 (已满) 29 | Rust编程语言社区2群 303838735 30 | (telegram群): 31 | ``` 32 | 33 | *进群必须附带您的github地址,否则不予通过* 34 | 35 | ### telegram社群 36 | 37 | rust社区深水群:[https://t.me/rust_deep_water](https://t.me/rust_deep_water) 38 | 39 | rust众: [https://t.me/rust_zh](https://t.me/rust_zh) 40 | 41 | 42 | ### 社区 43 | 44 | chat: https://chat.rust-china.org/ (使用github验证登录) 45 | 46 | blog: https://rust-china.org/ 47 | 48 | wiki: https://wiki.rust-china.org/ 49 | 50 | ## 版权规定 51 | 52 | 本书使用 `CC BY-SA 3.0` 协议,转载请注明地址。 53 | 54 | ## GitBook 生成 55 | 56 | 直接用: 57 | 58 | ``` 59 | gitbook serve 60 | ``` 61 | 62 | 即可。 63 | 64 | ## ChangeLog 65 | 66 | 1. 2016年3月31日,初稿完成。发布 v1.0 版。 67 | 2. 2016年5月2日,完成 1.1.0 版本。 68 | -------------------------------------------------------------------------------- /unsafe-rawpointer/raw-pointer.md: -------------------------------------------------------------------------------- 1 | # 裸指针 2 | 3 | **Rust**通过限制智能指针的行为保障了编译时安全,不过仍需要对指针做一些额外的操作。 4 | 5 | `*const T`和`*mut T`在**Rust**中被称为“裸指针”。它允许别名,允许用来写共享所有权的类型,甚至是内存安全的共享内存类型如:`Rc`和`Arc`,但是赋予你更多权利的同时意味着你需要担当更多的责任: 6 | 7 | * 不能保证指向有效的内存,甚至不能保证是非空的 8 | * 没有任何自动清除,所以需要手动管理资源 9 | * 是普通旧式类型,也就是说,它不移动所有权,因此**Rust**编译器不能保证不出像释放后使用这种bug 10 | * 缺少任何形式的生命周期,不像`&`,因此编译器不能判断出悬垂指针 11 | * 除了不允许直接通过`*const T`改变外,没有别名或可变性的保障 12 | 13 | ## 使用 14 | 15 | 创建一个裸指针: 16 | 17 | ```rust 18 | let a = 1; 19 | let b = &a as *const i32; 20 | 21 | let mut x = 2; 22 | let y = &mut x as *mut i32; 23 | ``` 24 | 25 | 解引用需要在`unsafe`中进行: 26 | 27 | ```rust 28 | let a = 1; 29 | let b = &a as *const i32; 30 | let c = unsafe { *b }; 31 | println!("{}", c); 32 | ``` 33 | 34 | `Box`的`into_raw`: 35 | 36 | ```rust 37 | let a: Box = Box::new(10); 38 | // 我们需要先解引用a,再隐式把 & 转换成 * 39 | let b: *const i32 = &*a; 40 | // 使用 into_raw 方法 41 | let c: *const i32 = Box::into_raw(a); 42 | ``` 43 | 44 | 如上说所,引用和裸指针之间可以隐式转换,但隐式转换后再解引用需要使用`unsafe`: 45 | 46 | ```rust 47 | // 显式 48 | let a = 1; 49 | let b: *const i32 = &a as *const i32; //或者let b = &a as *const i32; 50 | // 隐式 51 | let c: *const i32 = &a; 52 | unsafe { 53 | println!("{}", *c); 54 | } 55 | 56 | ``` 57 | -------------------------------------------------------------------------------- /flow/comment.md: -------------------------------------------------------------------------------- 1 | # 注释 2 | 3 | Rust 代码文件中,通常我们可以看到 3 种注释。 4 | 5 | - 行注释 6 | - 文档注释 7 | - 模块注释 8 | 9 | ## 行注释 10 | 11 | `//` 后的,直到行尾,都属于注释,不会影响程序的行为。 12 | 13 | ```rust 14 | // 创建一个绑定 15 | let x = 5; 16 | 17 | let y = 6; // 创建另一个绑定 18 | ``` 19 | 20 | ## 文档注释 21 | 22 | 文档注释使用 ```///```,一般用于函数或结构体(字段)的说明,置于要说明的对象上方。文档注释内部可使用markdown格式的标记语法,可用于 rustdoc 工具的自动文档提取。 23 | 24 | /// Adds one to the number given. 25 | /// 26 | /// # Examples 27 | /// 28 | /// ``` 29 | /// let five = 5; 30 | /// 31 | /// assert_eq!(6, add_one(5)); 32 | /// # fn add_one(x: i32) -> i32 { 33 | /// # x + 1 34 | /// # } 35 | /// ``` 36 | fn add_one(x: i32) -> i32 { 37 | x + 1 38 | } 39 | 40 | 41 | ## 模块注释 42 | 43 | 模块注释使用 ```//!```,用于说明本模块的功能。一般置于模块文件的头部。 44 | 45 | ```rust 46 | //! # The Rust Standard Library 47 | //! 48 | //! The Rust Standard Library provides the essential runtime 49 | //! functionality for building portable Rust software. 50 | ``` 51 | 52 | PS: 相对于 `///`, `//!` 用来注释包含它的项(也就是说,crate,模块或者函数),而不是位于它之后的项。 53 | 54 | 55 | ## 其它:兼容C语言的注释 56 | 57 | Rust 也支持兼容 C 的块注释写法:`/* */`。但是不推荐使用,请尽量不要使用这种注释风格(会被鄙视的)。 58 | 59 | ```rust 60 | /* 61 | let x = 42; 62 | println!("{}", x); 63 | */ 64 | ``` 65 | -------------------------------------------------------------------------------- /action/mysite/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate iron; 2 | extern crate router; 3 | extern crate rustc_serialize; 4 | 5 | use iron::prelude::*; 6 | use iron::status; 7 | use router::Router; 8 | use rustc_serialize::json; 9 | use std::io::Read; 10 | 11 | #[derive(RustcEncodable, RustcDecodable)] 12 | struct Greeting { 13 | msg: String 14 | } 15 | 16 | fn main() { 17 | let mut router = Router::new(); 18 | 19 | router.get("/", hello_world); 20 | router.post("/set", set_greeting); 21 | 22 | fn hello_world(_: &mut Request) -> IronResult { 23 | let greeting = Greeting { msg: "Hello, World".to_string() }; 24 | let payload = json::encode(&greeting).unwrap(); 25 | Ok(Response::with((status::Ok, payload))) 26 | } 27 | 28 | // Receive a message by POST and play it back. 29 | fn set_greeting(request: &mut Request) -> IronResult { 30 | let mut payload = String::new(); 31 | request.body.read_to_string(&mut payload); 32 | let request: Greeting = json::decode(&payload).unwrap(); 33 | let greeting = Greeting { msg: request.msg }; 34 | let payload = json::encode(&greeting).unwrap(); 35 | Ok(Response::with((status::Ok, payload))) 36 | } 37 | 38 | Iron::new(router).http("localhost:3000").unwrap(); 39 | } 40 | -------------------------------------------------------------------------------- /editors/atom.md: -------------------------------------------------------------------------------- 1 | # Atom 2 | 本文是rust的Atom编辑器配置。 3 | 横向对比一下,不得不说,Atom无论在易用性还是界面上都比前辈们要好的很多,对于Rust的配置,也是基本上可以做到开箱即用。 4 | 虽然本文独占一小节,但是其实能写的东西也就了了。 5 | 6 | - [自行配置](#自行配置) 7 | - [使用tokamak](#tokamak) 8 | 9 | ## 自行配置 10 | 11 | ## 准备工作 12 | 13 | 首先,你需要一个可执行的rustc编译器,一个cargo程序,一个已经编译好的racer程序和一份已经解压好的rust源码。 14 | 我们假定你已经将这三个程序安装完毕,并且能够自由的从命令行里调用他们。 15 | 16 | 另外,本文不讲解如何安装Atom,需要新安装的同学请自行前往[项目主页](https://github.com/atom/atom)安装。 17 | 18 | ps:无论是windows用户还是*nix用户都需要将以上三个程序加入你的PATH(Windows下叫Path)环境变量里。 19 | 20 | ## 需要安装的插件包 21 | 22 | 打开Atom,按Ctrl+Shift+p,搜索preference,打开Atom的配置中心,选择install选项卡。 23 | 24 | 依次安装`rust-api-docs-helper`/`racer`/`language-rust`/`linter-rust`/`linter`。 25 | 26 | 这里要单独说的一个就是linter,这是一个基础的lint组件包,atom的很多以linter为前缀的包都会依赖这个包,但是Atom并不会为我们自动的安装,因此需要我们自己去安装。 27 | 28 | ## 一点配置 29 | 30 | 以上,我们安装好了几个组件包,但是不要着急去打开一个Rust文件。你可能还需要一点点的配置。这里,我们在配置中心里打开`Packages`选项卡,在`Installed Packages`里搜索racer,并点击其`Setting`。 31 | 32 | 这里需要将racer的可执行文件的绝对路径填入`Path to the Racer executable`里。同时,我们还需要将rust源码文件夹下的src目录加入到`Path to the Rust source code directory`里。 33 | 34 | ## 完成安装 35 | 36 | 好了,就是这么简单。你现在可以打开任意一个rust文件就会发现源码高亮已经默认打开了,编辑一下,racer也能自动补全,*如果不能*,尝试一下用`F3`键来显式地呼出racer的补全。 37 | 38 | ## tokamak 39 | 40 | [tokamak](https://github.com/vertexclique/tokamak) 是一个使 atom 摇身一变为 rust IDE 的 atom 插件. 安装后 atom 即具有语法高亮, 代码补全与 Lint 等功能, 而且还有个不错的界面, 看起来确实像个 IDE. 你可以在 atom 中搜索 tokamak 并安装它. 41 | -------------------------------------------------------------------------------- /install/install_rust_on_mac_os.md: -------------------------------------------------------------------------------- 1 | # Rust for Mac OS 2 | 3 | Rust 支持主流的操作系统,Linux,Mac 和 windows。 4 | 5 | Rust 为 mac 用户提供了两种安装方式: 6 | 7 | ### 1、直接下载安装包: 8 | 9 | 直接下载安装包的话需要检查一下你当前操作系统是64位还是32位,分别下载对应的安装包。 10 | 查看操作系统请在终端执行如下命令: 11 | 12 | `uname -a` 13 | 14 | ![Mac-os-inofrmatoin](../images/get-mac-os-information.png) 15 | 16 | 如上图红色部分所示,如果是 **x86_64** 则证明是64位系统,需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-x86_64-apple-darwin.pkg)64位安装包; 17 | 如果是**x86-32**则需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-i686-apple-darwin.pkg)32位安装包 18 | 19 | 和安装普通的软件一样,直接运行安装包即可。 20 | 21 | 22 | *在书写本章时,最新的稳定版本为1.5.0,* 23 | 24 | ### 2、命令行一键安装: 25 | Rust 提供简单的一键安装,命令如下: 26 | 27 | `curl -sSf https://static.rust-lang.org/rustup.sh | sh` 28 | 29 | *此过程,有可能需要你输入几次密码* 30 | 31 | 你只需打开你的命令行执行如上代码就可以了。(注意,你可能需要一个梯子,否则会遇到一些类似*Could not resolve host: static.rust-lang.org*的错误) 32 | 33 | ### 3.验证安装: 34 | 如果你完成了上面任意一个步骤,请执行如下命令: 35 | 36 | `rustc --version` 37 | 38 | 如果看到如下信息,表明你安装成功: 39 | 40 | `rustc 1.5.0 (3d7cd77e4 2015-12-04)` 41 | 42 | 如果提示没有 *rustc* 命令,那么请回顾你是否有某个地方操作不对,请回过头来再看一遍文档。 43 | 44 | **注意** 45 | 46 | 除了稳定版之外,Rust 还提供了 Beta 和 Nightly 版本,下载地址如下: 47 | https://www.rust-lang.org/zh-CN/other-installers.html 48 | 49 | 如果你不想安装 Rust 在你的电脑上,但是你还是像尝试一下 rust,那么这里有一个在线的环境:http://play.rust-lang.org/ 50 | 51 | 中国科学技术大学镜像源包含 [rust-static](http://mirrors.ustc.edu.cn/rust-static/),梯子暂时出问题的同学可以尝试从这里下载编译器;除此之外,还有 Crates 源,详见[这里的说明](https://servers.ustclug.org/2016/01/mirrors-add-rust-crates/)。 52 | -------------------------------------------------------------------------------- /intoborrow/borrow.md: -------------------------------------------------------------------------------- 1 | # Borrow, BorrowMut, ToOwned 2 | 3 | ## Borrow 4 | 5 | `use std::borrow::Borrow;` 6 | 7 | `Borrow` 提供了一个方法 `.borrow()`。 8 | 9 | 对于一个类型为 `T` 的值 `foo`,如果 `T` 实现了 `Borrow`,那么,`foo` 可执行 `.borrow()` 操作,即 `foo.borrow()`。操作的结果,我们得到了一个类型为 `&U` 的新引用。 10 | 11 | `Borrow` 可以认为是 `AsRef` 的严格版本,它对普适引用操作的前后类型之间附加了一些其它限制。 12 | 13 | `Borrow` 的前后类型之间要求必须有内部等价性。不具有这个等价性的两个类型之间,不能实现 `Borrow`。 14 | 15 | `AsRef` 更通用,更普遍,覆盖类型更多,是 `Borrow` 的超集。 16 | 17 | 举例: 18 | 19 | ```rust 20 | use std::borrow::Borrow; 21 | 22 | fn check>(s: T) { 23 | assert_eq!("Hello", s.borrow()); 24 | } 25 | 26 | let s = "Hello".to_string(); 27 | 28 | check(s); 29 | 30 | let s = "Hello"; 31 | 32 | check(s); 33 | ``` 34 | 35 | ## BorrowMut 36 | 37 | `use std::borrow::BorrowMut;` 38 | 39 | `BorrowMut` 提供了一个方法 `.borrow_mut()`。它是 `Borrow` 的可变(mutable)引用版本。 40 | 41 | 对于一个类型为 `T` 的值 `foo`,如果 `T` 实现了 `BorrowMut`,那么,`foo` 可执行 `.borrow_mut()` 操作,即 `foo.borrow_mut()`。操作的结果我们得到类型为 `&mut U` 的一个可变(mutable)引用。 42 | 43 | 注:在转换的过程中,`foo` 会被可变(mutable)借用。 44 | 45 | ## ToOwned 46 | 47 | `use std::borrow::ToOwned;` 48 | 49 | `ToOwned` 为 `Clone` 的普适版本。它提供了 `.to_owned()` 方法,用于类型转换。 50 | 51 | 有些实现了 `Clone` 的类型 `T` 可以从引用状态实例 `&T` 通过 `.clone()` 方法,生成具有所有权的 `T` 的实例。但是它只能由 `&T` 生成 `T`。而对于其它形式的引用,`Clone` 就无能为力了。 52 | 53 | 而 `ToOwned` trait 能够从任意引用类型实例,生成具有所有权的类型实例。 54 | 55 | ## 参考 56 | 57 | - [http://doc.rust-lang.org/std/borrow/trait.Borrow.html](http://doc.rust-lang.org/std/borrow/trait.Borrow.html) 58 | -------------------------------------------------------------------------------- /install/install_rust_on_linux.md: -------------------------------------------------------------------------------- 1 | # Rust for Linux 2 | 3 | Rust 支持主流的操作系统,Linux,Mac和 windows。 4 | 5 | Rust 为Linux用户提供了两种安装方式: 6 | 7 | ### 1、直接下载安装包: 8 | 9 | 直接下载安装包的话需要检查一下你当前操作系统是64位还是32位,分别下载对应的安装包。 10 | 11 | 查看操作系统请在终端执行如下命令: 12 | 13 | `uname -a` 14 | 15 | 结果如下图所示: 16 | 17 | ![check system info](../images/install-on-linux-check-system.png) 18 | 19 | 如上图所示,如果是 **x86_64** 则证明是64位系统,需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-x86_64-unknown-linux-gnu.tar.gz)64位安装包; 20 | 21 | 如果是**x86-32**则需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-i686-unknown-linux-gnu.tar.gz)32位安装包 22 | 23 | 下载安装包后解压运行即可。*在书写本章时,最新的稳定版本为1.5.0,* 24 | 解压:`tar -zxvf rust-1.5.0-x86_64-unknown-linux-gnu.tar.gz` 25 | 26 | 解压完进入对应的目录:`cd rust-1.5.0-x86_64-unknown-linux-gnu` 27 | 执行 `./install.sh` 28 | 29 | 上述命令执行完成后会打印: **Rust is ready to roll.** 表明安装成功 30 | 31 | 此时执行: `rustc --version`, 你会看到对应的 rust 版本信息,如下图所示: 32 | 33 | ![Success and show rust version info](../images/install-on-linux-rust-success.png) 34 | 35 | ### 2、命令行一键安装: 36 | Rust 提供简单的一键安装,命令如下: 37 | 38 | `curl -sSf https://static.rust-lang.org/rustup.sh | sh` 39 | 40 | 打开终端执行如上命令即可。 41 | 42 | 43 | **注意** 44 | 45 | 除了稳定版之外,Rust 还提供了 Beta 和 Nightly 版本,下载地址如下: 46 | https://www.rust-lang.org/zh-CN/other-installers.html 47 | 48 | 如果你不想安装 Rust 在你的电脑上,但是你还是像尝试一下 rust,那么这里有一个在线的环境:http://play.rust-lang.org/ 49 | 50 | 中国科学技术大学镜像源包含 [rust-static](http://mirrors.ustc.edu.cn/rust-static/),梯子暂时出问题的同学可以尝试从这里下载编译器;除此之外。还有 Crates 源,详见[这里的说明](https://servers.ustclug.org/2016/01/mirrors-add-rust-crates/)。 51 | -------------------------------------------------------------------------------- /io/preface.md: -------------------------------------------------------------------------------- 1 | # 输入与输出 2 | 3 | 输入与输出可以说是一个实用程序的最基本要求,没有输入输出的程序是没有什么卵用的。虽然输入输出被函数式编程语言鄙称为副作用,但正是这个副作用才赋予了程序实用性,君不见某著名函数式语言之父称他主导设计的函数式语言"[is useless](https://www.youtube.com/watch?v=iSmkqocn0oQ)"。这章我们就来谈谈输入输出副作用。 4 | 5 | ## 读写 Trait 6 | 7 | 输入最基本的功能是读(Read),输出最基本的功能是写(Write)。标准库里面把怎么读和怎么写抽象出来归到了 `Read` 和 `Write` 两个接口里面,实现了 `Read` 接口的叫 reader,而实现了 `Write` 的叫 writer。Rust里面的 Trait 比其它语言里面的接口更好的一个地方是 Trait 可以带默认实现,比如用户定义的 reader 只需要实现 `read` 一个方法就可以调用 `Read` trait 里面的任意其它方法,而 writer 也只需要实现 `write` 和 `flush` 两个方法。 8 | 9 | Read 和 Write 这两个 Trait 都有定义了好多方法,具体可以参考标准库 API 文档中的[Read](http://doc.rust-lang.org/stable/std/io/trait.Read.html) 和 [Write](http://doc.rust-lang.org/stable/std/io/trait.Write.html) 10 | 11 | Read 由于每调用一次 `read` 方法都会调用一次系统API与内核交互,效率比较低,如果给 reader 增加一个 buffer,在调用时 `read` 方法时多读一些数据放在 buffer 里面,下次调用 `read` 方法时就有可能只需要从 buffer 里面取数据而不用调用系统API了,从而减少了系统调用次数提高了读取效率,这就是所谓的 `BufRead` Trait。一个普通的 reader 通过 `io::BufReader::new(reader)` 或者 `io::BufReader::with_capacity(bufSize, reader)` 就可以得到一个 BufReader 了,显然这两个创建 BufReader 的函数一个是使用默认大小的 buffer 一个可以指定 buffer 大小。BufReader 比较常用的两个方法是按行读: `read_line(&mut self, buf: &mut String) -> Result` 和 `lines(&mut self) -> Lines`,从函数签名上就可以大概猜出函数的用法所以就不啰嗦了,需要注意的是后者返回的是一个迭代器。详细说明直接看 API 文档中的[BufRead](http://doc.rust-lang.org/stable/std/io/trait.BufRead.html) 12 | 13 | 同样有 `BufWriter` 只不过由于其除了底层加了 buffer 之外并没有增加新的写方法,所以并没有专门的 `BufWrite` Trait,可以通过 `io::BufWriter::new(writer)` 或 `io::BufWriter::with_capacity(bufSize, writer)` 创建 `BufWriter`。 14 | 15 | 输入与输出接口有了,我们接下来看看实际应用中最常用的两类 reader 和 writer:标准输入/输出,文件输入/输出 16 | -------------------------------------------------------------------------------- /appendix/glossary.md: -------------------------------------------------------------------------------- 1 | # 附录I-术语表 2 | 3 | * ADT(Algebraic Data Type:代数数据类型): 4 | * ARC(Atomic Reference Counting:原子引用计数): 5 | * associated function(关联函数): 6 | * associated type(关联类型): Trait 里面可以有关联类型 7 | * AST(Abstract Syntax Tree:抽象语法树): 8 | * benchmark(基准测试): 9 | * bitwise copy: 10 | * borrow(借用): 11 | * bounds(约束): 12 | * box: 13 | * byte string(): 14 | * cargo: 15 | * cast: 16 | * channel: 17 | * coercion: 18 | * constructor(构造器): 19 | * consumer: 20 | * copy: 21 | * crate: 22 | * dangling pointer: 23 | * deref(解引用): 24 | * derive: 25 | * designator(指示符): 26 | * destructor(): 27 | * destructure(析构): 28 | * diverging function(发散函数): 29 | * drop: 30 | * DST(Dynamically Sized Type): 31 | * dynamic dispatch(动态分发): 32 | * enum(): 33 | * feature gate(特性开关): nightly 版本中有特性开关可以启用一些实验性质的特性 34 | * FFI(Foreign Function Interface:外部函数接口): 35 | * guard: 36 | * hygiene: 37 | * inline function(内联函数): 38 | * item: 39 | * iterator(迭代器): 40 | * iterator adaptor(迭代器适配器): 41 | * lifetime(生命周期): 42 | * lifetime elision: 43 | * literal string(): 44 | * macro by example: 45 | * memberwise copy: 46 | * module(模块) 47 | * move: 48 | * option: 49 | * ownership(所有权): 50 | * panic(崩溃): 51 | * phantom type: 52 | * primitive type(基本类型): 整型、浮点、布尔等基本类型 53 | * procedural macro: 54 | * RAII(): 55 | * raw string: 56 | * raw pointer: 57 | * RC(Reference Counting:引用计数) 58 | * result: 59 | * shadowing: 60 | * static dispatch(静态分发): 61 | * slice(切片): 某种数据类型的视图,例如 string, vector 62 | * statement(): 与 expression 相区别 63 | * trait: 64 | * trait object: 65 | * tuple(元组): 66 | * UFCS(Universal Function Call Syntax) 67 | * unit(): 68 | * unwind: 69 | * unwrap(): 70 | * wrap: 71 | -------------------------------------------------------------------------------- /io/file-io.md: -------------------------------------------------------------------------------- 1 | # 文件输入与输出 2 | 3 | 文件 `std::fs::File` 本身实现了 `Read` 和 `Write` trait,所以文件的输入输出非常简单,只要得到一个 `File` 类型实例就可以调用读写接口进行文件输入与输出操作了。而要得到 `File` 就得让操作系统打开(open)或新建(create)一个文件。还是拿例子来说明 4 | 5 | ```rust 6 | use std::io; 7 | use std::io::prelude::*; 8 | use std::fs::File; 9 | 10 | // create file and write something 11 | fn create_file(filename: &str, buf: &[u8]) -> io::Result<()> { 12 | let mut f = try!(File::create(filename)); 13 | try!(f.write(&buf)); 14 | Ok(()) 15 | } 16 | 17 | // read from file to String 18 | fn read_file(filename: &str, buf: &mut String) -> io::Result<()> { 19 | let mut f = try!(File::open(filename)); 20 | try!(f.read_to_string(&buf)); 21 | Ok(()) 22 | } 23 | 24 | fn main() { 25 | let f = "foo.txt"; 26 | let mut buf = String::new(); 27 | match create_file(f, b"Hello, World!") { 28 | Ok(()) => { 29 | match read_file(f, &mut buf) { 30 | Ok(()) => {println!("{}", buf);}, 31 | Err(e) => {println!("{}", e);}, 32 | }; 33 | }, 34 | Err(e) => {println!("{}", e);}, 35 | } 36 | } 37 | ``` 38 | 39 | 文件操作上面 Rust 与其它语言处理方式有些不一样,其它语言一般把读写选项作为函数参数传给 open 函数,而 Rust 则是在 option 上面调用 open 函数。 [`std::fs::OpenOptions`](http://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html) 是一个 builder,通过 new 函数创建后,可以链式调用设置打开文件的选项,是 read, write, append, truncate 还是 create 等,OpenOptions 构建完成后就可以再接着调用 open 方法了,看下下面的例子就明白了 40 | 41 | ```rust 42 | use std::fs::OpenOptions; 43 | 44 | let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); 45 | ``` 46 | 47 | Rust 这种用 builder pattern 来设置打开文件选项,相比于将选项以字符作为参数传给 open 函数的一个优点是可以让编译器保证检查选项合法性,不用等到运行时才发现手抖把 read-mode 的 `r` 写成了 `t`。 48 | -------------------------------------------------------------------------------- /testing/bench.md: -------------------------------------------------------------------------------- 1 | # 性能测试 2 | 3 | 单元测试是用来校验程序的正确性的,然而,程序能正常运行后,往往还需要测试程序(一部分)的执行速度,这时,f就需要用到性能测试。 4 | 通常来讲,所谓性能测试,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。 5 | 6 | 下面直接说明如何使用。 7 | 8 | ``` 9 | cargo new benchit 10 | cd benchit 11 | ``` 12 | 13 | 编辑 `src/lib.rs` 文件,在里面添加如下代码: 14 | 15 | ```rust 16 | #![feature(test)] 17 | 18 | extern crate test; 19 | 20 | pub fn add_two(a: i32) -> i32 { 21 | a + 2 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use super::*; 27 | use test::Bencher; 28 | 29 | #[test] 30 | fn it_works() { 31 | assert_eq!(4, add_two(2)); 32 | } 33 | 34 | #[bench] 35 | fn bench_add_two(b: &mut Bencher) { 36 | b.iter(|| add_two(2)); 37 | } 38 | } 39 | ``` 40 | 41 | 注意: 42 | 43 | 1. 这里虽然使用了 `extern crate test;`,但是项目的 `Cargo.toml` 文件中依赖区并不需要添加对 `test` 的依赖; 44 | 2. 评测函数 `fn bench_add_two(b: &mut Bencher) {}` 上面使用 `#[bench]` 做标注,同时函数接受一个参数,`b` 就是 Rust 提供的评测器。这个写法是固定的。 45 | 46 | 然后,在工程根目录下,执行 47 | 48 | ``` 49 | cargo bench 50 | ``` 51 | 52 | 输出结果类似如下: 53 | 54 | ``` 55 | $ cargo bench 56 | Compiling benchit v0.0.1 (file:///home/mike/tmp/benchit) 57 | Running target/release/benchit-91b3e234d4ed382a 58 | 59 | running 2 tests 60 | test tests::it_works ... ignored 61 | test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) 62 | 63 | test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured 64 | ``` 65 | 66 | 可以看到,Rust 的性能测试是以纳秒 ns 为单位。 67 | 68 | 写测评代码的时候,需要注意以下一些点: 69 | 70 | 1. 只把你需要做性能测试的代码(函数)放在评测函数中; 71 | 2. 对于参与做性能测试的代码(函数),要求每次测试做同样的事情,不要做累积和改变外部状态的操作; 72 | 3. 参数性能测试的代码(函数),执行时间不要太长。太长的话,最好分成几个部分测试。这也方便找出性能瓶颈所在地方。 73 | 74 | -------------------------------------------------------------------------------- /marker/sendsync.md: -------------------------------------------------------------------------------- 1 | # Send 和 Sync 2 | 3 | `std::marker` 模块中,有两个 trait:`Send` 和 `Sync`,它们与多线程安全相关。 4 | 5 | 标记为 `marker trait` 的 trait,它实际就是一种约定,没有方法的定义,也没有关联元素(associated items)。仅仅是一种约定,实现了它的类型必须满足这种约定。一种类型是否加上这种约定,要么是编译器的行为,要么是人工手动的行为。 6 | 7 | `Send` 和 `Sync` 在大部分情况下(针对 Rust 的基础类型和 std 中的大部分类型),会由编译器自动推导出来。对于不能由编译器自动推导出来的类型,要使它们具有 `Send` 或 `Sync` 的约定,可以由人手动实现。实现的时候,必须使用 `unsafe` 前缀,因为 Rust 默认不信任程序员,由程序员自己控制的东西,统统标记为 `unsafe`,出了问题(比如,把不是线程安全的对象加上 `Sync` 约定)由程序员自行负责。 8 | 9 | 它们的定义如下: 10 | 11 | 如果 `T: Send`,那么将 `T` 传到另一个线程中时(按值传送),不会导致数据竞争或其它不安全情况。 12 | 13 | 1. `Send` 是对象可以安全发送到另一个执行体中; 14 | 2. `Send` 使被发送对象可以和产生它的线程解耦,防止原线程将此资源释放后,在目标线程中使用出错(use after free)。 15 | 16 | 如果 `T: Sync`,那么将 `&T` 传到另一个线程中时,不会导致数据竞争或其它不安全情况。 17 | 18 | 1. `Sync` 是可以被同时多个执行体访问而不出错; 19 | 2. `Sync` 防止的是竞争; 20 | 21 | 推论: 22 | 23 | 1. `T: Sync` 意味着 `&T: Send`; 24 | 3. `Sync + Copy = Send`; 25 | 4. 当 `T: Send` 时,可推导出 `&mut T: Send`; 26 | 4. 当 `T: Sync` 时,可推导出 `&mut T: Sync`; 27 | 5. 当 `&mut T: Send` 时,不能推导出 `T: Send`; 28 | 29 | (注:`T`, `&T`, `&mut T`,`Box` 等都是不同的类型) 30 | 31 | 32 | 具体的类型: 33 | 34 | 1. 原始类型(比如: u8, f64),都是 `Sync`,都是 `Copy`,因此都是 `Send`; 35 | 2. 只包含原始类型的复合类型,都是 `Sync`,都是 `Copy`,因此都是 `Send`; 36 | 3. 当 `T: Sync`,`Box`, `Vec` 等集合类型是 `Sync`; 37 | 4. 具有内部可变性的的指针,不是 `Sync` 的,比如 `Cell`, `RefCell`, `UnsafeCell`; 38 | 5. `Rc` 不是 `Sync`。因为只要一做 `&Rc` 操作,就会克隆一个新引用,它会以非原子性的方式修改引用计数,所以是不安全的; 39 | 6. 被 `Mutex` 和 `RWLock` 锁住的类型 `T: Send`,是 `Sync` 的; 40 | 7. 原始指针(`*mut`, `*const`)既不是 `Send` 也不是 `Sync`; 41 | 42 | 43 | Rust 正是通过这两大武器:`所有权和生命周期` + `Send 和 Sync`(本质上为类型系统)来为并发编程提供了安全可靠的基础设施。使得程序员可以放心在其上构建稳健的并发模型。这也正是 Rust 的核心设计观的体现:内核只提供最基础的原语,真正的实现能分离出去就分离出去。并发也是如此。 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /concurrency-parallel-thread/parallel.md: -------------------------------------------------------------------------------- 1 | ## 并行 2 | 理论上并行和语言并没有什么关系,所以在理论上的并行方式,都可以尝试用Rust来实现。本小节不会详细全面地介绍具体的并行理论知识,只介绍用Rust如何来实现相关的并行模式。 3 | 4 | Rust的一大特点是,可以保证“线程安全”。而且,没有性能损失。更有意思的是,Rust编译器实际上只有`Send` `Sync`等基本抽象,而对“线程” “锁” “同步” 等基本的并行相关的概念一无所知,这些概念都是由库实现的。这意味着Rust实现并行编程可以有比较好的扩展性,可以很轻松地用库来支持那些常见的并行编程模式。 5 | 下面,我们以一个例子来演示一下,Rust如何将线程安全/执行高效/使用简单结合起来的。 6 | 7 | 在图形编程中,我们经常要处理归一化的问题: 即把一个范围内的值,转换到范围1内的值。比如把一个颜色值255归一后就是1。假设我们有一个表示颜色值的数组要进行归一,用非并行化的方式来处理非常简单,可以自行尝试。下面我们将采用并行化的方式来处理,把数组中的值同时分开给多个线程一起并行归一化处理。 8 | 9 | ```rust 10 | extern crate rayon; 11 | 12 | use rayon::prelude::*; 13 | 14 | fn main() { 15 | let mut colors = [-20.0f32, 0.0, 20.0, 40.0, 16 | 80.0, 100.0, 150.0, 180.0, 200.0, 250.0, 300.0]; 17 | println!("original: {:?}", &colors); 18 | 19 | colors.par_iter_mut().for_each(|color| { 20 | let c : f32 = if *color < 0.0 { 21 | 0.0 22 | } else if *color > 255.0 { 23 | 255.0 24 | } else { 25 | *color 26 | }; 27 | *color = c / 255.0; 28 | }); 29 | println!("transformed: {:?}", &colors); 30 | } 31 | ``` 32 | 33 | 运行结果: 34 | 35 | ``` 36 | original: [-20, 0, 20, 40, 80, 100, 150, 180, 200, 250, 300] 37 | transformed: [0, 0, 0.078431375, 0.15686275, 0.3137255, 0.39215687, 0.5882353, 0.7058824, 0.78431374, 0.98039216, 1] 38 | ``` 39 | 40 | 以上代码是不是很简单。调用`par_iter_mut`获得一个并行执行的具有写权限的迭代器,`for_each`对每个元素执行一个操作。仅此而已。 41 | 我们能这么轻松地完成这个任务,原因是我们引入了 [rayon](https://github.com/nikomatsakis/rayon/) 这个库。它把所有的脏活累活都干完了,把清晰安全易用的接口暴露出来给了我们。Rust还可以完全以库的形式,实现异步IO、协程等更加高阶的并行程序开发模式。 42 | 43 | 为了更深入的加深对Rust并发编程的理解和实践,还安排了一个挑战任务:实现一个Rust版本的MapReduce模式。值得你挑战。 44 | -------------------------------------------------------------------------------- /function/arguement.md: -------------------------------------------------------------------------------- 1 | # 函数参数 2 | ## 参数声明 3 | rust的函数参数声明和一般的变量声明相仿,也是参数名后加冒号,冒号后跟参数类型,不过不需要`let`关键字。需要注意的是,普通变量声明(let语句)是可以省略变量类型的,而函数参数的声明则不能省略参数类型。 4 | 来看一个简单例子: 5 | 6 | ```rust 7 | fn main() { 8 | say_hi("ruster"); 9 | } 10 | 11 | fn say_hi(name: &str) { 12 | println!("Hi, {}", name); 13 | } 14 | ``` 15 | 16 | 上例中,`say_hi`函数拥有一个参数,名为`name`,类型为`&str`。 17 | 18 | ## 将函数作为参数 19 | 在rust中,函数是一等公民(可以储存在变量/数据结构中,可以作为参数传入函数,可以作为返回值),所以rust的函数参数不仅可以是一般的类型,也可以是函数。如: 20 | 21 | ```rust 22 | fn main() { 23 | let xm = "xiaoming"; 24 | let xh = "xiaohong"; 25 | say_what(xm, hi); 26 | say_what(xh, hello); 27 | } 28 | 29 | fn hi(name: &str) { 30 | println!("Hi, {}.", name); 31 | } 32 | 33 | fn hello(name: &str) { 34 | println!("Hello, {}.", name); 35 | } 36 | 37 | fn say_what(name: &str, func: fn(&str)) { 38 | func(name) 39 | } 40 | ``` 41 | 42 | 上例中,`hi`函数和`hello`函数都是只有一个`&str`类型的参数且没有返回值。而`say_what`函数则有两个参数,一个是`&str`类型,另一个则是函数类型(function type),它是只有一个`&str`类型参数且没有返回值的函数类型。 43 | 44 | ## 模式匹配 45 | 支持模式匹配,让rust平添了许多的灵活性,用起来也是十分的舒爽。模式匹配不仅可以用在变量声明(let语句)中,也可以用在函数参数声明中,如: 46 | 47 | ```rust 48 | fn main() { 49 | let xm = ("xiaoming", 54); 50 | let xh = ("xiaohong", 66); 51 | print_id(xm); 52 | print_id(xh); 53 | print_name(xm); 54 | print_age(xh); 55 | print_name(xm); 56 | print_age(xh); 57 | } 58 | 59 | fn print_id((name, age): (&str, i32)) { 60 | println!("I'm {},age {}.", name, age); 61 | } 62 | 63 | fn print_age((_, age): (&str, i32)) { 64 | println!("My age is {}", age); 65 | } 66 | 67 | fn print_name((name,_): (&str, i32)) { 68 | println!("I am {}", name); 69 | } 70 | ``` 71 | 72 | 上例是一个元组(Tuple)匹配的例子,当然也可以是其他可在let语句中使用的类型。参数的模式匹配跟let语句的匹配一样,也可以使用下划线来表示丢弃一个值。 73 | -------------------------------------------------------------------------------- /action/db/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate postgres; 2 | 3 | use postgres::{Connection, SslMode}; 4 | use postgres::types::FromSql; 5 | use postgres::Result as PgResult; 6 | 7 | 8 | struct Person { 9 | id: i32, 10 | name: String, 11 | data: Option> 12 | } 13 | 14 | 15 | pub fn connect() -> Connection{ 16 | let dsn = "postgresql://postgres:2015@localhost/rust_example"; 17 | Connection::connect(dsn, SslMode::None).unwrap() 18 | } 19 | 20 | pub fn insert_info(conn : &Connection,title : &str, body: &str){ 21 | 22 | let stmt = match conn.prepare("insert into blog (title, body) values ($1, $2)") { 23 | Ok(stmt) => stmt, 24 | Err(e) => { 25 | println!("Preparing query failed: {:?}", e); 26 | return; 27 | } 28 | }; 29 | stmt.execute(&[&title, &body]).expect("Inserting blogposts failed"); 30 | } 31 | 32 | 33 | pub fn query(conn: &Connection,query: &str) ->PgResult 34 | where T: FromSql { 35 | println!("Executing query: {}", query); 36 | let stmt = try!(conn.prepare(query)); 37 | let rows = try!(stmt.query(&[])); 38 | &rows.iter().next().unwrap(); 39 | let row = &rows.iter().next().unwrap(); 40 | //rows.iter().next().unwrap() 41 | row.get_opt(2).unwrap() 42 | 43 | } 44 | 45 | pub fn query_all(conn: &Connection,query: &str){ 46 | println!("Executing query: {}", query); 47 | for row in &conn.query(query,&[]).unwrap(){ 48 | let person = Person{ 49 | id: row.get(0), 50 | name: row.get(1), 51 | data: row.get(2) 52 | }; 53 | println!("Found person {}", person.name); 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /flow/condition.md: -------------------------------------------------------------------------------- 1 | # 条件分支 2 | 3 | - if 4 | - if let 5 | - match 6 | 7 | ## if 表达式 8 | 9 | Rust 中的 if 表达式基本就是如下几种形式: 10 | 11 | ```rust 12 | // 形式 1 13 | if expr1 { 14 | 15 | } 16 | 17 | // 形式 2 18 | if expr1 { 19 | 20 | } 21 | else { 22 | 23 | } 24 | 25 | // 形式 3 26 | if expr1 { 27 | 28 | } 29 | else if expr2 { 30 | // else if 可多重 31 | } 32 | else { 33 | 34 | } 35 | 36 | ``` 37 | 38 | 相对于 C 系语言,Rust 的 if 表达式的显著特点是: 39 | 40 | 1. 判断条件不用小括号括起来; 41 | 2. 它是表达式,而不是语句。 42 | 43 | 鉴于上述第二点,因为是表达式,所以我们可以写出如下代码: 44 | 45 | ```rust 46 | let x = 5; 47 | 48 | let y = if x == 5 { 49 | 10 50 | } else { 51 | 15 52 | }; // y: i32 53 | ``` 54 | 55 | 或者压缩成一行: 56 | 57 | ```rust 58 | let x = 5; 59 | 60 | let y = if x == 5 { 10 } else { 15 }; // y: i32 61 | ``` 62 | 63 | ## if let 64 | 65 | 我们在代码中常常会看到 `if let` 成对出现,这实际上是一个 match 的简化用法。直接举例来说明: 66 | 67 | ```rust 68 | let x = Some(5); 69 | 70 | if let Some(y) = x { 71 | println!("{}", y); // 这里输出为:5 72 | } 73 | 74 | let z = if let Some(y) = x { 75 | y 76 | } 77 | else { 78 | 0 79 | }; 80 | // z 值为 5 81 | 82 | ``` 83 | 84 | 上面代码等价于 85 | 86 | ```rust 87 | let x = Some(5); 88 | match x { 89 | Some(y) => println!("{}", y), 90 | None => () 91 | } 92 | 93 | let z = match x { 94 | Some(y) => y, 95 | None => 0 96 | }; 97 | ``` 98 | 99 | 设计这个特性的目的是,在条件判断的时候,直接做一次模式匹配,方便代码书写,使代码更紧凑。 100 | 101 | ## match 102 | 103 | Rust 中没有类似于 C 的 `switch` 关键字,但它有用于模式匹配的 `match`,能实现同样的功能,并且强大太多。 104 | 105 | match 的使用非常简单,举例如下: 106 | 107 | ```rust 108 | let x = 5; 109 | 110 | match x { 111 | 1 => { 112 | println!("one") 113 | }, 114 | 2 => println!("two"), 115 | 3 => println!("three"), 116 | 4 => println!("four"), 117 | 5 => println!("five"), 118 | _ => println!("something else"), 119 | } 120 | ``` 121 | 注意,match 也是一个表达式。match 后面会专门论述,请参见 **模式匹配** 这一章。 122 | -------------------------------------------------------------------------------- /quickstart/vector-string.md: -------------------------------------------------------------------------------- 1 | # 数组、动态数组和字符串 2 | ## 数组和动态数组 3 | ### 数组 array 4 | Rust 使用数组存储相同类型的数据集。 5 | `[T; N]`表示一个拥有 T 类型,N 个元素的数组。数组的大小是固定。 6 | 7 | **例子:** 8 | 9 | ```rust 10 | fn main() { 11 | let mut array: [i32; 3] = [0; 3]; 12 | 13 | array[1] = 1; 14 | array[2] = 2; 15 | 16 | assert_eq!([1, 2], &array[1..]); 17 | 18 | // This loop prints: 0 1 2 19 | for x in &array { 20 | println!("{} ", x); 21 | } 22 | } 23 | ``` 24 | 25 | ### 动态数组 Vec 26 | 动态数组是一种基于堆内存申请的连续动态数据类型,拥有 O(1) 时间复杂度的索引、压入(push)、弹出(pop)。 27 | 28 | **例子:** 29 | 30 | ```rust 31 | //创建空Vec 32 | let v: Vec = Vec::new(); 33 | //使用宏创建空Vec 34 | let v: Vec = vec![]; 35 | //创建包含5个元素的Vec 36 | let v = vec![1, 2, 3, 4, 5]; 37 | //创建十个零 38 | let v = vec![0; 10]; 39 | //创建可变的Vec,并压入元素3 40 | let mut v = vec![1, 2]; 41 | v.push(3); 42 | //创建拥有两个元素的Vec,并弹出一个元素 43 | let mut v = vec![1, 2]; 44 | let two = v.pop(); 45 | //创建包含三个元素的可变Vec,并索引一个值和修改一个值 46 | let mut v = vec![1, 2, 3]; 47 | let three = v[2]; 48 | v[1] = v[1] + 5; 49 | ``` 50 | 51 | ## 字符串 52 | Rust 里面有两种字符串类型。`String` 和 `str`。 53 | 54 | ### &str 55 | `str` 类型基本上不怎么使用,通常使用 `&str` 类型,它其实是 `[u8]` 类型的切片形式 `&[u8]`。这是一种固定大小的字符串类型。 56 | 常见的的字符串字面值就是 `&'static str` 类型。这是一种带有 `'static` 生命周期的 &str 类型。 57 | 58 | **例子:** 59 | 60 | ```rust 61 | // 字符串字面值 62 | let hello = "Hello, world!"; 63 | 64 | // 附带显式类型标识 65 | let hello: &'static str = "Hello, world!"; 66 | ``` 67 | 68 | ### String 69 | `String` 是一个带有的 `vec:Vec` 成员的结构体,你可以理解为 `str` 类型的动态形式。 70 | 它们的关系相当于 `[T]` 和 `Vec` 的关系。 71 | 显然 `String` 类型也有压入和弹出。 72 | 73 | **例子:** 74 | 75 | ```rust 76 | // 创建一个空的字符串 77 | let mut s = String::new(); 78 | // 从 `&str` 类型转化成 `String` 类型 79 | let mut hello = String::from("Hello, "); 80 | // 压入字符和压入字符串切片 81 | hello.push('w'); 82 | hello.push_str("orld!"); 83 | 84 | // 弹出字符。 85 | let mut s = String::from("foo"); 86 | assert_eq!(s.pop(), Some('o')); 87 | assert_eq!(s.pop(), Some('o')); 88 | assert_eq!(s.pop(), Some('f')); 89 | assert_eq!(s.pop(), None); 90 | ``` 91 | -------------------------------------------------------------------------------- /unsafe-rawpointer/unsafe.md: -------------------------------------------------------------------------------- 1 | # unsafe 2 | 3 | **Rust**的内存安全依赖于强大的类型系统和编译时检测,不过它并不能适应所有的场景。 4 | 首先,所有的编程语言都需要跟外部的“不安全”接口打交道,调用外部库等,在“安全”的Rust下是无法实现的; 其次,“安全”的Rust无法高效表示复杂的数据结构,特别是数据结构内部有各种指针互相引用的时候;再次, 5 | 事实上还存在着一些操作,这些操作是安全的,但不能通过编译器的验证。 6 | 7 | 因此在安全的Rust背后,还需要`unsafe`的支持。 8 | 9 | `unsafe`块能允许程序员做的额外事情有: 10 | 11 | * 解引用一个裸指针`*const T`和`*mut T` 12 | 13 | ```rust 14 | let x = 5; 15 | let raw = &x as *const i32; 16 | let points_at = unsafe { *raw }; 17 | println!("raw points at {}", points_at); 18 | ``` 19 | 20 | * 读写一个可变的静态变量`static mut` 21 | 22 | ```rust 23 | static mut N: i32 = 5; 24 | unsafe { 25 | N += 1; 26 | println!("N: {}", N); 27 | } 28 | ``` 29 | 30 | * 调用一个不安全函数 31 | 32 | ```rust 33 | unsafe fn foo() { 34 | //实现 35 | } 36 | fn main() { 37 | unsafe { 38 | foo(); 39 | } 40 | } 41 | ``` 42 | 43 | ## 使用`unsafe` 44 | 45 | `unsafe fn`不安全函数标示如果调用它可能会违反**Rust**的内存安全语意: 46 | 47 | ```rust 48 | unsafe fn danger_will_robinson() { 49 | // 实现 50 | } 51 | ``` 52 | 53 | `unsafe block`不安全块可以在其中调用不安全的代码: 54 | 55 | ```rust 56 | unsafe { 57 | // 实现 58 | } 59 | ``` 60 | 61 | `unsafe trait`不安全trait及它们的实现,所有实现它们的具体类型有可能是不安全的: 62 | 63 | ```rust 64 | unsafe trait Scary { } 65 | unsafe impl Scary for i32 {} 66 | ``` 67 | 68 | ## safe != no bug 69 | 70 | 对于**Rust**来说禁止你做任何不安全的事是它的本职,不过有些是编写代码时的`bug`,它们并不属于“内存安全”的范畴: 71 | 72 | * 死锁 73 | * 内存或其他资源溢出 74 | * 退出未调用析构函数 75 | * 整型溢出 76 | 77 | 使用`unsafe`时需要注意一些特殊情形: 78 | 79 | * 数据竞争 80 | * 解引用空裸指针和悬垂裸指针 81 | * 读取未初始化的内存 82 | * 使用裸指针打破指针重叠规则 83 | * `&mut T`和`&T`遵循LLVM范围的`noalias`模型,除了如果`&T`包含一个`UnsafeCell`的话。不安全代码必须不能违反这些重叠(aliasing)保证 84 | * 不使用`UnsafeCell`改变一个不可变值/引用 85 | * 通过编译器固有功能调用未定义行为: 86 | * 使用`std::ptr::offset`(offset功能)来索引超过对象边界的值,除了允许的末位超出一个字节 87 | * 在重叠(overlapping)缓冲区上使用`std::ptr::copy_nonoverlapping_memory`(memcpy32/memcpy64功能) 88 | * 原生类型的无效值,即使是在私有字段/本地变量中: 89 | * 空/悬垂引用或装箱 90 | * `bool`中一个不是`false`(0)或`true`(1)的值 91 | * `enum`中一个并不包含在类型定义中判别式 92 | * `char`中一个代理字(surrogate)或超过char::MAX的值 93 | * `str`中非UTF-8字节序列 94 | * 在外部代码中使用Rust或在Rust中使用外部语言 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /intoborrow/deref.md: -------------------------------------------------------------------------------- 1 | # Deref 2 | 3 | `Deref` 是 `deref` 操作符 `*` 的 trait,比如 `*v`。 4 | 5 | 一般理解,`*v` 操作,是 `&v` 的反向操作,即试图由资源的引用获取到资源的拷贝(如果资源类型实现了 `Copy`),或所有权(资源类型没有实现 `Copy`)。 6 | 7 | Rust 中,本操作符行为可以重载。这也是 Rust 操作符的基本特点。本身没有什么特别的。 8 | 9 | ## 强制隐式转换(coercion) 10 | 11 | `Deref` 神奇的地方并不在本身 `解引` 这个意义上,Rust 的设计者在它之上附加了一个特性:`强制隐式转换`,这才是它神奇之处。 12 | 13 | 这种隐式转换的规则为: 14 | 15 | 一个类型为 `T` 的对象 `foo`,如果 `T: Deref`,那么,相关 `foo` 的某个智能指针或引用(比如 `&foo`)在应用的时候会自动转换成 `&U`。 16 | 17 | 粗看这条规则,貌似有点类似于 `AsRef`,而跟 `解引` 似乎风马牛不相及。实际里面有些玄妙之处。 18 | 19 | Rust 编译器会在做 `*v` 操作的时候,自动先把 `v` 做引用归一化操作,即转换成内部通用引用的形式 `&v`,整个表达式就变成 `*&v`。这里面有两种情况: 20 | 21 | 1. 把其它类型的指针(比如在库中定义的,`Box`, `Rc`, `Arc`, `Cow` 等),转成内部标准形式 `&v`; 22 | 2. 把多重 `&` (比如:`&&&&&&&v`),简化成 `&v`(通过插入足够数量的 `*` 进行解引)。 23 | 24 | 所以,它实际上在解引用之前做了一个引用的归一化操作。 25 | 26 | 为什么要转呢? 因为编译器设计的能力是,只能够对 `&v` 这种引用进行解引用。其它形式的它不认识,所以要做引用归一化操作。 27 | 28 | 使用引用进行过渡也是为了能够防止不必要的拷贝。 29 | 30 | 下面举一些例子: 31 | 32 | ```rust 33 | fn foo(s: &str) { 34 | // borrow a string for a second 35 | } 36 | 37 | // String implements Deref 38 | let owned = "Hello".to_string(); 39 | 40 | // therefore, this works: 41 | foo(&owned); 42 | ``` 43 | 44 | 因为 `String` 实现了 `Deref`。 45 | 46 | ```rust 47 | use std::rc::Rc; 48 | 49 | fn foo(s: &str) { 50 | // borrow a string for a second 51 | } 52 | 53 | // String implements Deref 54 | let owned = "Hello".to_string(); 55 | let counted = Rc::new(owned); 56 | 57 | // therefore, this works: 58 | foo(&counted); 59 | ``` 60 | 因为 `Rc` 实现了 `Deref`。 61 | 62 | ```rust 63 | fn foo(s: &[i32]) { 64 | // borrow a slice for a second 65 | } 66 | 67 | // Vec implements Deref 68 | let owned = vec![1, 2, 3]; 69 | 70 | foo(&owned); 71 | ``` 72 | 73 | 因为 `Vec` 实现了 `Deref`。 74 | 75 | ```rust 76 | struct Foo; 77 | 78 | impl Foo { 79 | fn foo(&self) { println!("Foo"); } 80 | } 81 | 82 | let f = &&Foo; 83 | 84 | f.foo(); 85 | (&f).foo(); 86 | (&&f).foo(); 87 | (&&&&&&&&f).foo(); 88 | ``` 89 | 90 | 上面那几种函数的调用,效果是一样的。 91 | 92 | 93 | `coercion` 的设计,是 Rust 中仅有的类型隐式转换,设计它的目的,是为了简化程序的书写,让代码不至于过于繁琐。把人从无尽的类型细节中解脱出来,让书写 Rust 代码变成一件快乐的事情。 94 | -------------------------------------------------------------------------------- /safe/safety.md: -------------------------------------------------------------------------------- 1 | # 安全(Safety) 2 | 3 | 本章不讲解任何语言知识点,而是对 Rust 安全理念的一些总结性说明。 4 | 5 | 安全,本身是一个相当大的话题。安全性,本身也需要一个局部性的定义。 6 | 7 | Rust 的定义中,凡是 **可能** 会导致程序内存使用出错的特性,都被认为是 **不安全的(unsafe)**。反之,则是 **安全的(safe)**。 8 | 9 | 基于这种定义,C 语言,基本是不安全的语言(它是众多不安全特性的集合。特别是指针相关特性,多线程相关特性)。 10 | 11 | Rust 的这个定义,隐含了一个先决假设:人之初,性本恶。人是不可靠的,人是会犯错误的,即 Rust 不相信人的实施过程。在这一点上,C 语言的理念与之完全相反:C 语言完全相信人,人之初,性本善,由人进行完全地控制。 12 | 13 | 根据 Rust 的定义,C 语言几乎是不安全的代名字。但是,从本质上来说,一段程序是否安全,并不由开发它的语言决定。用 C 语言开发出的程序,不一定就是不安全的代码,只不过相对来说,需要花更多的精力进行良好的设计和长期的实际运行验证。Rust 使开发出安全可靠的代码相对容易了。 14 | 15 | 世界本身是肮脏的。正如,纯函数式语言中还必须有用于处理副作用的 `Monad` 存在一样,Rust 仅凭安全的特性集合,也是无法处理世界的所有结构和问题的。所以,Rust 中,还有 `unsafe` 部分的存在。实际上,Rust 的 std 本身也是建立在大量 `unsafe` 代码的基础之上的。所以,世界就是纯粹建立在不纯粹之上,“安全”建立在“不安全”之上。 16 | 17 | 因此,Rust 本身可以被认为是两种编程语言的混合:`Safe Rust` 和 `Unsafe Rust`。 18 | 19 | 只使用 `Safe Rust` 的情况下,你不需要担心任何类型安全性和内存安全性的问题。你永远不用忍受空指针,悬挂指针或其它可能的未定义行为的干扰。 20 | 21 | `Unsafe Rust` 在 `Safe Rust` 的所有特性上,只给程序员开放了以下四种能力: 22 | 23 | 1. 对原始指针进行解引(Dereference raw pointers); 24 | 2. 调用 `unsafe` 函数(包括 C 函数,内部函数,和原始分配器); 25 | 3. 实现 `unsafe` traits; 26 | 4. 修改(全局)静态变量。 27 | 28 | 上述这四种能力,如果误用的话,会导致一些未定义行为,具有不确定后果,很容易引起程序崩溃。 29 | 30 | Rust 中定义的不确定性行为有如下一些: 31 | 32 | 1. 对空指针或悬挂指针进行解引用; 33 | 2. 读取未初始化的内存; 34 | 3. 破坏指针重命名规则(比如同一资源的 `&mut` 引用不能出现多次,`&mut` 与 `&` 不能同时出现); 35 | 4. 产生无效的原生值: 36 | - 空指针,悬挂指针; 37 | - bool 值不是 0 或 1; 38 | - 未定义的枚举取值; 39 | - char 值超出取值范围 [0x0, 0xD7FF] 和 [0xE000, 0x10FFFF]; 40 | - 非 utf-8 字符串; 41 | 5. Unwinding 到其它语言中; 42 | 6. 产生一个数据竞争。 43 | 44 | 以下一些情况,Rust 认为不属于安全性的处理范畴,即认为它们是“安全”的: 45 | 46 | 1. 死锁; 47 | 2. 存在竞争条件; 48 | 3. 内存泄漏; 49 | 4. 调用析构函数失败; 50 | 5. 整数溢出; 51 | 6. 程序被中断; 52 | 7. 删除产品数据库(:D); 53 | 54 | 55 | 56 | 57 | ## 参考 58 | 59 | 下面一些链接,给出了安全性更详细的讲解(部分未来会有对应的中文翻译)。 60 | 61 | - [Unsafe](http://doc.rust-lang.org/book/unsafe.html) 62 | - [Meet Safe and Unsafe](http://doc.rust-lang.org/nightly/nomicon/meet-safe-and-unsafe.html) 63 | - [How Safe and Unsafe Interact](http://doc.rust-lang.org/nightly/nomicon/safe-unsafe-meaning.html) 64 | - [蓦然回首万事空 ————空指针漫谈](http://jimhuang.cn/2015/09/12/%E8%93%A6%E7%84%B6%E5%9B%9E%E9%A6%96%E4%B8%87%E4%BA%8B%E7%A9%BA%20%E2%80%94%E2%80%94%E2%80%94%E2%80%94%E7%A9%BA%E6%8C%87%E9%92%88%E6%BC%AB%E8%B0%88/) 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /editors/visualstudio.md: -------------------------------------------------------------------------------- 1 | # Visual Studio 2 | 3 | 本文是使用VisualRust和VS GDB Debugger / VisualGDB 完成在VisualStudio中,编辑和调试Rust程序。 4 | 5 | ## 安装Rust, Cargo 6 | 7 | 首先需要下载Rust, 下载地址https://www.rust-lang.org/downloads.html 8 | 9 | 这里一定要下windows GNU ABI的版本, 因为我们要用GDB来调试. 10 | 11 | ![](../images/editor-visualstudio-download.png) 12 | 13 | 另外,机器上也需要安装Visual Studio2013或2015。 14 | 安装完Rust,打开命令行,执行 15 | cargo install racer 16 | 17 | ![](../images/editor-visualstudio-racer.png) 18 | 19 | Racer是用来做Rust自动完成的,会在VisualRust使用。这里我们使用rust编译的racer, 并不用VisualRust里自带的racer,因为它太旧了. 20 | 另外需要下载Rust源代码,设置 21 | RUST_SRC_PATH为Rust源代码src的目录 22 | 23 | ![](../images/editor-visualstudio-racersc.png) 24 | 25 | ## 安装VisualRust和VS GDB Debugger 26 | 27 | 做完上述工作,就可以安装VisualRust和VS GDB Debugger,在这里下载 28 | https://github.com/PistonDevelopers/VisualRust 29 | https://visualstudiogallery.msdn.microsoft.com/35dbae07-8c1a-4f9d-94b7-bac16cad9c01 30 | 31 | VisualGDB可在这里购买 32 | http://www.visualgdb.com/ 33 | 34 | ## 编译Rust项目 35 | 36 | 新建Rust项目 37 | ![](../images/editor-visualstudio-newproject.png) 38 | 在tool, option里设置racer和rust_src_path 39 | ![](../images/editor-visualstudio-settings.png) 40 | 这时候就可以在写代码的时候就可以自动提示了。像下面这样 41 | ![](../images/editor-visualstudio-autocomplete.png) 42 | 43 | ## 用VS GDB Debugger调试Rust项目 44 | 45 | ok,愉快的开始你的Rust之旅吧。下面开始使用VS GDB Debugger调试Rust. 46 | 47 | 48 | 在解决方案中,添加GDB调试项目 49 | ![](../images/editor-visualstudio-GDBproject.png) 50 | 51 | 设置需要调试的程序所在的目录和文件名 52 | ![](../images/editor-visualstudio-GDBproject-settings.png) 53 | 54 | 设置需要调试的程序的编译命令,此处用rustc,也可以使用cargo编译 55 | ![](../images/editor-visualstudio-GDBproject-settings2.png) 56 | 57 | 将需要调试的程序的源代码添加到项目目录下 58 | ![](../images/editor-visualstudio-add-files.png) 59 | 60 | 打开源代码文件并设置断点信息,将项目设置为启动项目并选择Local GDB即可开始调试 61 | ![](../images/editor-visualstudio-set-breakpoints.png) 62 | 63 | ![](../images/editor-visualstudio-debugging2.png) 64 | 65 | 66 | ## 用VisualGDB调试Rust项目 67 | 68 | 69 | Build完Rust程序,点击debug, 选择quick debug with gdb 70 | ![](../images/editor-visualstudio-quickdebug.png) 71 | 72 | 然后在里面选择MingW和exe的路径 73 | 74 | ![](../images/editor-visualstudio-setdebugger.png) 75 | 76 | 点击Debug,开始你的调试生活吧 77 | 78 | ![](../images/editor-visualstudio-debugging.png) 79 | -------------------------------------------------------------------------------- /match/match.md: -------------------------------------------------------------------------------- 1 | # match关键字 2 | 模式匹配,多出现在函数式编程语言之中,为其复杂的类型系统提供一个简单轻松的解构能力。比如从enum等数据结构中取出数据等等,但是在书写上,相对比较复杂。我们来看一个例子: 3 | 4 | ```rust 5 | enum Direction { 6 | East, 7 | West, 8 | North, 9 | South, 10 | } 11 | 12 | fn main() { 13 | let dire = Direction::South; 14 | match dire { 15 | Direction::East => println!("East"), 16 | Direction::North | Direction::South => { 17 | println!("South or North"); 18 | }, 19 | _ => println!("West"), 20 | }; 21 | } 22 | ``` 23 | 24 | 这是一个没什么实际意义的程序,但是能清楚的表达出match的用法。看到这里,你肯定能想起一个常见的控制语句——`switch`。没错,match可以起到和switch相同的作用。不过有几点需要注意: 25 | 26 | 1. match所罗列的匹配,必须穷举出其所有可能。当然,你也可以用 **_** 这个符号来代表其余的所有可能性情况,就类似于switch中的`default`语句。 27 | 2. match的每一个分支都必须是一个表达式,并且,除非一个分支一定会触发panic,这些分支的所有表达式的最终返回值类型必须相同。 28 | 29 | 关于第二点,有的同学可能不明白。这么说吧,你可以把match整体视为一个表达式,既然是一个表达式,那么就一定能求得它的结果。因此,这个结果当然就可以被赋予一个变量咯。 30 | 看代码: 31 | 32 | ```rust 33 | enum Direction { 34 | East, 35 | West, 36 | North, 37 | South, 38 | } 39 | 40 | fn main() { 41 | // let d_panic = Direction::South; 42 | let d_west = Direction::West; 43 | let d_str = match d_west { 44 | Direction::East => "East", 45 | Direction::North | Direction::South => { 46 | panic!("South or North"); 47 | }, 48 | _ => "West", 49 | }; 50 | 51 | println!("{}", d_str); 52 | } 53 | ``` 54 | 55 | ## 解构初窥 56 | 57 | match还有一个非常重要的作用就是对现有的数据结构进行解构,轻易的可以拿出其中的数据部分来。 58 | 比如,以下是比较常见的例子: 59 | 60 | ```rust 61 | enum Action { 62 | Say(String), 63 | MoveTo(i32, i32), 64 | ChangeColorRGB(u16, u16, u16), 65 | } 66 | 67 | fn main() { 68 | let action = Action::Say("Hello Rust".to_string()); 69 | match action { 70 | Action::Say(s) => { 71 | println!("{}", s); 72 | }, 73 | Action::MoveTo(x, y) => { 74 | println!("point from (0, 0) move to ({}, {})", x, y); 75 | }, 76 | Action::ChangeColorRGB(r, g, _) => { 77 | println!("change color into '(r:{}, g:{}, b:0)', 'b' has been ignored", 78 | r, g, 79 | ); 80 | } 81 | } 82 | } 83 | ``` 84 | 85 | 有人说了,从这来看也并不觉得match有多神奇啊!别急,请看下一小节——>[模式](pattern.md) 86 | -------------------------------------------------------------------------------- /editors/sublime.md: -------------------------------------------------------------------------------- 1 | # Sublime 2 | 3 | Sublime Text是一款非常有名的文本编辑器,其本身也具备强大的插件机制。通过配置各种插件可以在使用Sublime Text编辑rust代码时获得更加良好的支持。 4 | 5 | 本文主要展示在已经预装rust的Windows环境下的安装,如果您还没有安装rust,请先参照本书的[安装章节](../install/install_rust_on_windows.md)安装rust。 6 | 7 | ## 安装 8 | 9 | ### Sublime Text3安装 10 | 11 | 请在 [Sublime Text3官网](http://www.sublimetext.com/3)上选择适合当前机器版本的Sublime Text版本进行下载和安装。 12 | 13 | ### rust的安装 14 | 15 | 请在rust官网的[下载页面](https://www.rust-lang.org/downloads.html)下载rust的源代码压缩包并在本地解压缩安装,在稍后的配置环节我们将会用到这个路径。如果国内下载速度过慢,可以考虑使用中科大的[镜像](http://mirrors.ustc.edu.cn/)下载rust源码包。 16 | 17 | ### 下载Rust并编译代码提示插件racer 18 | 19 | 具体安装和编译内容请查看本章第一节的[安装准备](../editors/before.md),请牢记编译后的racer.exe文件路径,在稍后的配置环节中我们将用到它。 20 | 21 | ## 配置 22 | 23 | ### Sublime Text3相关插件安装 24 | 25 | #### 安装Package Control 26 | 27 | Sublime Text3在安装各种插件前需要先安装Package Control,如果您的编辑器已安装Package Control请跳过本段直接安装rust相关插件。 28 | 29 | 您可以查看[Package Control官网](https://packagecontrol.io/installation)学习如何安装。 30 | 也可以直接在编辑器中使用 `ctrl+~` 快捷键启动控制台,粘贴以下代码并回车进行安装。 31 | 32 | ```shell 33 | 34 | import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by) 35 | 36 | ``` 37 | 38 | #### rust相关插件 39 | 40 | 在编辑器下使用快捷键 `ctrl+shift+p` 启动命令行工具,输入Install Package按回车进入插件安装,选择或输入插件名称并回车即可完成插件的安装。 41 | 42 | 使用上述方式安装Rust插件\(rust语法高亮\)、RustAutoComplete\(rust代码提示和自动补全插件\)。 43 | 44 | 此时安装尚未完成,我们需要将本地的 racer.exe配置进RustAutoComplete插件中。打开编辑器顶端的Preferences选项卡,依次 Preferences->Package Settings->RustAutoComplete->Settings-User 来打开 RustAutoComplete 的配置文件,在文件中配置以下信息并保存。 45 | 46 | ```shell 47 | { 48 | "racer": "E:/soft/racer-master/target/release/racer.exe", 49 | "search_paths": [ "E:/soft/rustc-1.7.0/src" ] 50 | } 51 | ``` 52 | 53 | 其中racer是编译后的racer.exe程序的绝对路径。search_paths是rust源码文件下src目录的绝对路径。 54 | 55 | 编辑器重启后插件即可生效。 56 | 57 | ## 快速编译 58 | 59 | Sublime本身支持多种编译系统,在Tools选项卡下的Build System中选择Rust或者Cargo作为编译系统,选中后使用快捷键 `ctrl+B` 即可对代码进行快速编译。 60 | 61 | -------------------------------------------------------------------------------- /any/any.md: -------------------------------------------------------------------------------- 1 | # Any和反射 2 | 3 | 熟悉Java的同学肯定对Java的反射能力记忆犹新,同样的,Rust也提供了运行时反射的能力。但是,这里有点小小的不同,因为 Rust 不带 VM 不带 Runtime ,因此,其提供的反射更像是一种编译时反射。 4 | 5 | 因为,Rust只能对 `'static` 生命周期的变量(常量)进行反射! 6 | 7 | ## 举个例子 8 | 9 | 我们会有这样的需求,去某些路径里加载配置文件。我们可能提供一个配置文件路径,好吧,这是个字符串(`String`)。但是,当我想要传入多个配置文件的路径的时候怎们办?理所应当的,我们传入了一个数组。 10 | 11 | 这下可坏了……Rust不支持重载啊!于是有人就很单纯的写了两个函数~~! 12 | 13 | 其实不用……我们只需要这么写…… 14 | 15 | ```rust 16 | use std::any::Any; 17 | use std::fmt::Debug ; 18 | 19 | fn load_config(value: &T) -> Vec{ 20 | let mut cfgs: Vec= vec![]; 21 | let value = value as &Any; 22 | match value.downcast_ref::() { 23 | Some(cfp) => cfgs.push(cfp.clone()), 24 | None => (), 25 | }; 26 | 27 | match value.downcast_ref::>() { 28 | Some(v) => cfgs.extend_from_slice(&v), 29 | None =>(), 30 | } 31 | 32 | if cfgs.len() == 0 { 33 | panic!("No Config File"); 34 | } 35 | cfgs 36 | } 37 | 38 | fn main() { 39 | let cfp = "/etc/wayslog.conf".to_string(); 40 | assert_eq!(load_config(&cfp), vec!["/etc/wayslog.conf".to_string()]); 41 | let cfps = vec!["/etc/wayslog.conf".to_string(), 42 | "/etc/wayslog_sec.conf".to_string()]; 43 | assert_eq!(load_config(&cfps), 44 | vec!["/etc/wayslog.conf".to_string(), 45 | "/etc/wayslog_sec.conf".to_string()]); 46 | } 47 | ``` 48 | 49 | 我们来重点分析一下中间这个函数: 50 | 51 | ```rust 52 | fn load_config(value: &T) -> Vec{..} 53 | ``` 54 | 55 | 首先,这个函数接收一个泛型`T`类型,`T`必须实现了`Any`和`Debug`。 56 | 57 | 这里可能有同学疑问了,你不是说只能反射 `'static` 生命周期的变量么?我们来看一下`Any`限制: 58 | 59 | ```rust 60 | pub trait Any: 'static + Reflect { 61 | fn get_type_id(&self) -> TypeId; 62 | } 63 | ``` 64 | 65 | 看,`Any`在定义的时候就规定了其生命周期,而`Reflect`是一个Marker,默认所有的Rust类型都会实现他!注意,这里不是所有原生类型,而是所有类型。 66 | 67 | 好的,继续,由于我们无法判断出传入的参数类型,因此,只能从运行时候反射类型。 68 | 69 | ```rust 70 | let value = value as &Any; 71 | ``` 72 | 73 | 首先,我们需要将传入的类型转化成一个 `trait Object`, 当然了,你高兴的话用 `UFCS` 也是可以做的,参照本章最后的附录。 74 | 75 | 这样,value 就可以被堪称一个 Any 了。然后,我们通过 `downcast_ref` 来进行类型推断。如果类型推断成功,则 value 就会被转换成原来的类型。 76 | 77 | 有的同学看到这里有点懵,为什么你都转换成 Any 了还要转回来? 78 | 79 | 其实,转换成 Any 是为了有机会获取到他的类型信息,转换回来,则是为了去使用这个值本身。 80 | 81 | 最后,我们对不同的类型处以不同的处理逻辑。最终,一个反射函数就完成了。 82 | 83 | ## 说说注意的地方 84 | 85 | 需要注意的是,Rust本身提供的反射能力并不是很强大。相对而言只能作为一个辅助的手段。并且,其只能对`'static`周期进行反射的限制,的确限制了其发挥。还有一点需要注意的是,Rust的反射只能被用作类型推断,绝对不能被用作接口断言! 86 | 87 | 啥,你问原因?因为写不出来啊…… 88 | -------------------------------------------------------------------------------- /data-structure/linked_list.md: -------------------------------------------------------------------------------- 1 | # 链表 2 | 3 | ## 链表简介 4 | 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 由于不必须按顺序存储,链表在给定位置插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是在有序数据中查找一个节点或者访问特定下标的节点则需要O(n)的时间,而线性表相应的时间复杂度分别是O(logn)和O(1)。 5 | 6 | >使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在内存或磁盘上的顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。 7 | 8 | 下面看我们一步步实现链表: 9 | 10 | ## 定义链表结构 11 | 12 | ```rust 13 | use List::*; 14 | 15 | enum List { 16 | // Cons: 包含一个元素和一个指向下一个节点的指针的元组结构 17 | Cons(u32, Box), 18 | // Nil: 表示一个链表节点的末端 19 | Nil, 20 | } 21 | ``` 22 | 23 | ## 实现链表的方法 24 | 25 | ```rust 26 | impl List { 27 | // 创建一个空链表 28 | fn new() -> List { 29 | // `Nil` 是 `List`类型的。因为前面我们使用了 `use List::*;` 30 | // 所以不需要 List::Nil 这样使用 31 | Nil 32 | } 33 | 34 | // 在前面加一个元素节点,并且链接旧的链表和返回新的链表 35 | fn prepend(self, elem: u32) -> List { 36 | // `Cons` 也是 List 类型的 37 | Cons(elem, Box::new(self)) 38 | } 39 | 40 | // 返回链表的长度 41 | fn len(&self) -> u32 { 42 | // `self` 的类型是 `&List`, `*self` 的类型是 `List`, 43 | // 匹配一个类型 `T` 好过匹配一个引用 `&T` 44 | match *self { 45 | // 因为`self`是借用的,所以不能转移 tail 的所有权 46 | // 因此使用 tail 的引用 47 | Cons(_, ref tail) => 1 + tail.len(), 48 | // 基本规则:所以空的链表长度都是0 49 | Nil => 0 50 | } 51 | } 52 | 53 | // 返回连链表的字符串表达形式 54 | fn stringify(&self) -> String { 55 | match *self { 56 | Cons(head, ref tail) => { 57 | // `format!` 和 `print!` 很像 58 | // 但是返回一个堆上的字符串去替代打印到控制台 59 | format!("{}, {}", head, tail.stringify()) 60 | }, 61 | Nil => { 62 | format!("Nil") 63 | }, 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | ## 代码测试 70 | 71 | ```rust 72 | fn main() { 73 | let mut list = List::new(); 74 | 75 | list = list.prepend(1); 76 | list = list.prepend(2); 77 | list = list.prepend(3); 78 | 79 | println!("linked list has length: {}", list.len()); 80 | println!("{}", list.stringify()); 81 | } 82 | ``` 83 | 84 | ## 练习 85 | 86 | 基于以上代码实现一个双向循环链表。 87 | 88 | >双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。 89 | >循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。 90 | -------------------------------------------------------------------------------- /data-structure/graph.md: -------------------------------------------------------------------------------- 1 | # 图 2 | 3 | ## 图的存储结构 4 | 5 | 图的存储结构除了要存储图中各个顶点的本身信息外,同时还要存储顶点与顶点之间的所有关系(边的信息),因此,图的结构比较复杂,很难以数据元素在存储区中的物理位置来表示元素之间的关系,但也正是由于其任意的特性,故物理表示方法很多。常用的图的存储结构有邻接矩阵、邻接表等。 6 | 7 | ## 邻接矩阵表示法 8 | 9 | 对于一个具有n个结点的图,可以使用n*n的矩阵(二维数组)来表示它们间的邻接关系。矩阵 A(i,j) = 1 表示图中存在一条边 (Vi,Vj),而A(i,j)=0表示图中不存在边 (Vi,Vj)。 10 | 实际编程时,当图为不带权图时,可以在二维数组中存放 bool 值。 11 | 12 | * A(i,j) = true 表示存在边 (Vi,Vj), 13 | * A(i,j) = false 表示不存在边 (Vi,Vj); 14 | 15 | 16 | 当图带权值时,则可以直接在二维数值中存放权值,A(i,j) = null 表示不存在边 (Vi,Vj)。 17 | 18 | 下面看看我们使用邻接矩阵实现的图结构: 19 | 20 | ```rust 21 | #[derive(Debug)] 22 | struct Node { 23 | nodeid: usize, 24 | nodename: String, 25 | } 26 | 27 | #[derive(Debug,Clone)] 28 | struct Edge { 29 | edge: bool, 30 | } 31 | 32 | #[derive(Debug)] 33 | struct Graphadj { 34 | nodenums: usize, 35 | graphadj: Vec>, 36 | } 37 | 38 | impl Node { 39 | fn new(nodeid: usize, nodename: String) -> Node { 40 | Node{ 41 | nodeid: nodeid, 42 | nodename: nodename, 43 | } 44 | } 45 | } 46 | impl Edge { 47 | fn new() -> Edge { 48 | Edge{ 49 | edge: false, 50 | } 51 | } 52 | fn have_edge() -> Edge { 53 | Edge{ 54 | edge: true, 55 | } 56 | } 57 | } 58 | 59 | impl Graphadj { 60 | fn new(nums:usize) -> Graphadj { 61 | Graphadj { 62 | nodenums: nums, 63 | graphadj: vec![vec![Edge::new();nums]; nums], 64 | } 65 | } 66 | 67 | fn insert_edge(&mut self, v1: Node, v2:Node) { 68 | match v1.nodeid < self.nodenums && v2.nodeid { 70 | self.graphadj[v1.nodeid][v2.nodeid] = Edge::have_edge(); 71 | //下面这句注释去掉相当于把图当成无向图 72 | //self.graphadj[v2.nodeid][v1.nodeid] = Edge::have_edge(); 73 | } 74 | false => { 75 | panic!("your nodeid is bigger than nodenums!"); 76 | } 77 | } 78 | } 79 | } 80 | 81 | fn main() { 82 | let mut g = Graphadj::new(2); 83 | let v1 = Node::new(0, "v1".to_string()); 84 | let v2 = Node::new(1, "v2".to_string()); 85 | g.insert_edge(v1,v2); 86 | println!("{:?}", g); 87 | } 88 | ``` 89 | 90 | ## 邻接表表示法 91 | 92 | 邻接表是图的一种最主要存储结构,用来描述图上的每一个点。 93 | 94 | >**实现方式:**对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。 95 | 96 | * 在有向图中,描述每个点向别的节点连的边(点 a->点 b 这种情况)。 97 | * 在无向图中,描述每个点所有的边(点 a->点 b这种情况) 98 | 99 | 与邻接表相对应的存图方式叫做边集表,这种方法用一个容器存储所有的边。 100 | 101 | ## **练习:** 102 | 实现链接表表示法的图结构。 103 | -------------------------------------------------------------------------------- /editors/before.md: -------------------------------------------------------------------------------- 1 | # 前期准备 2 | 3 | ## 下载 Rust 源代码(供 racer 使用) 4 | 5 | ### 从github下载 6 | 7 | `git clone https://github.com/rust-lang/rust.git` 8 | 9 | ### 从官网下载源代码包 10 | 11 | 下载地址: `https://static.rust-lang.org/dist/rustc-nightly-src.tar.gz` 12 | 13 | ### 使用rustup下载(推荐) 14 | 15 | 使用rustup获取源码最大的好处在于可以使用`rustup update`随时获取最新版源码,~~而且特别省事,~~执行以下命令获取源码 16 | ``` 17 | rustup component add rust-src 18 | ``` 19 | ## racer 20 | racer是一个由rust的爱好者提供的rust自动补全和语法分析工具,被用来提供基本的补全功能和定义跳转功能。其本身完全由rust写成,补全功能已经比较完善了。 21 | 22 | 我们可以通过如下的方式获取它: 23 | 24 | ### cargo自动安装 25 | 在rust 1.5版本以后,其安装包自带的cargo工具已经支持了cargo install命令,这个命令可以帮助我们通过简单的方式获取到`racer`的最新版。 26 | 27 | 你可以通过以下命令安装`racer`最新版,目前已知在Linux、Unix和Windows上适用 28 | 29 | ``` 30 | cargo install racer 31 | ``` 32 | 33 | ### 编译安装 34 | 35 | 事实上我更推荐有条件的用户通过这种方式安装,因为自己实战操作一遍总是有些收获的。~~(帅气可爱的DCjanus表示怀疑)~~ 36 | 37 | #### 下载源码 38 | 39 | 首先,我们需要下载racer的源码 40 | 41 | ``` 42 | git clone https://github.com/phildawes/racer.git 43 | ``` 44 | 45 | #### 进行编译 46 | 47 | 然后,进入目录然后进行编译 48 | 49 | ``` 50 | cd racer && cargo build --release 51 | ``` 52 | 53 | 这样,我们会得到racer的二进制文件在 `target/release/racer`目录 54 | 55 | #### 设置环境变量 56 | 57 | 为了对Rust标准库进行补全,racer需要获取Rust源码路径。 58 | 59 | 设置名为`RUST_SRC_PATH`的环境变量为`[path_to_your_rust_source]/src` 60 | 61 | 其中`[path_to_your_rust_source]`表示源码所在文件夹,使用rustup获取Rust源码的情况下`[path_to_your_rust_source]`默认为`~/.multirust/toolchains/[your-toolchain]/lib/rustlib/src/rust/src` 62 | 63 | ### 测试 64 | 65 | 请重新打开终端,并进入到关闭之前的路径。 66 | 执行如下代码: 67 | linux: 68 | 69 | ``` 70 | ./target/release/racer complete std::io::B 71 | ``` 72 | 73 | windows: 74 | 75 | ``` 76 | target\release\racer complete std::io::B 77 | ``` 78 | 79 | 你将会看到racer的提示,这表示racer已经执行完成了。 80 | 81 | 82 | ## 安装 rustfmt 83 | 84 | `cargo install rustfmt` 85 | 86 | ## Rust Langular Server (RLS) 87 | 88 | `Rust Langular Server`(下文简称`RLS`)可以为很多IDE或编辑器提供包括不限于自动补全、跳转定义、重命名、跳转类型的功能支持。 89 | 90 | 使用rustup安装步骤如下: 91 | 92 | 1. 保证`rustup`为最新版 93 | ``` 94 | rustup self update 95 | ``` 96 | 2. 升级工具链(并不要求设置`nightly`为默认,但需要保证安装了`nightly`工具链) 97 | ``` 98 | rustup update nightly 99 | ``` 100 | 3. 正式安装RLS 101 | ``` 102 | rustup component add rls --toolchain nightly 103 | rustup component add rust-analysis --toolchain nightly 104 | rustup component add rust-src --toolchain nightly 105 | ``` 106 | 4. 设置环境变量 107 | 如果在安装Racer时没有设置名为`RUST_SRC_PATH`的环境变量,请参考前文进行设置。 108 | 109 | **截至当前(2017年7月15日),`RLS`仍然处于alpha阶段,随着项目变动,安装步骤可能会由较大变化,本文中提及的RLS安装方法可能在较短的时间内过时,建议跟随官方安装指导进行安装。** 110 | 111 | **该项目托管地址:[https://github.com/rust-lang-nursery/rls](https://github.com/rust-lang-nursery/rls)** 112 | -------------------------------------------------------------------------------- /rcarc/mutex.md: -------------------------------------------------------------------------------- 1 | # Mutex 与 RwLock 2 | 3 | ## Mutex 4 | 5 | `Mutex` 意为互斥对象,用来保护共享数据。Mutex 有下面几个特征: 6 | 7 | 1. `Mutex` 会等待获取锁令牌(token),在等待过程中,会阻塞线程。直到锁令牌得到。同时只有一个线程的 `Mutex` 对象获取到锁; 8 | 2. `Mutex` 通过 `.lock()` 或 `.try_lock()` 来尝试得到锁令牌,被保护的对象,必须通过这两个方法返回的 `RAII` 守卫来调用,不能直接操作; 9 | 3. 当 `RAII` 守卫作用域结束后,锁会自动解开; 10 | 4. 在多线程中,`Mutex` 一般和 `Arc` 配合使用。 11 | 12 | 示例: 13 | 14 | ```rust 15 | use std::sync::{Arc, Mutex}; 16 | use std::thread; 17 | use std::sync::mpsc::channel; 18 | 19 | const N: usize = 10; 20 | 21 | // Spawn a few threads to increment a shared variable (non-atomically), and 22 | // let the main thread know once all increments are done. 23 | // 24 | // Here we're using an Arc to share memory among threads, and the data inside 25 | // the Arc is protected with a mutex. 26 | let data = Arc::new(Mutex::new(0)); 27 | 28 | let (tx, rx) = channel(); 29 | for _ in 0..10 { 30 | let (data, tx) = (data.clone(), tx.clone()); 31 | thread::spawn(move || { 32 | // The shared state can only be accessed once the lock is held. 33 | // Our non-atomic increment is safe because we're the only thread 34 | // which can access the shared state when the lock is held. 35 | // 36 | // We unwrap() the return value to assert that we are not expecting 37 | // threads to ever fail while holding the lock. 38 | let mut data = data.lock().unwrap(); 39 | *data += 1; 40 | if *data == N { 41 | tx.send(()).unwrap(); 42 | } 43 | // the lock is unlocked here when `data` goes out of scope. 44 | }); 45 | } 46 | 47 | rx.recv().unwrap(); 48 | ``` 49 | 50 | ### `lock` 与 `try_lock` 的区别 51 | 52 | `.lock()` 方法,会等待锁令牌,等待的时候,会阻塞当前线程。而 `.try_lock()` 方法,只是做一次尝试操作,不会阻塞当前线程。 53 | 54 | 当 `.try_lock()` 没有获取到锁令牌时,会返回 `Err`。因此,如果要使用 `.try_lock()`,需要对返回值做仔细处理(比如,在一个循环检查中)。 55 | 56 | 57 | __点评__:Rust 的 Mutex 设计成一个对象,不同于 C 语言中的自旋锁用两条分开的语句的实现,更安全,更美观,也更好管理。 58 | 59 | 60 | ## RwLock 61 | 62 | `RwLock` 翻译成 `读写锁`。它的特点是: 63 | 64 | 1. 同时允许多个读,最多只能有一个写; 65 | 2. 读和写不能同时存在; 66 | 67 | 比如: 68 | 69 | ```rust 70 | use std::sync::RwLock; 71 | 72 | let lock = RwLock::new(5); 73 | 74 | // many reader locks can be held at once 75 | { 76 | let r1 = lock.read().unwrap(); 77 | let r2 = lock.read().unwrap(); 78 | assert_eq!(*r1, 5); 79 | assert_eq!(*r2, 5); 80 | } // read locks are dropped at this point 81 | 82 | // only one write lock may be held, however 83 | { 84 | let mut w = lock.write().unwrap(); 85 | *w += 1; 86 | assert_eq!(*w, 6); 87 | } // write lock is dropped here 88 | ``` 89 | 90 | ### 读写锁的方法 91 | 92 | 1. `.read()` 93 | 2. `.try_read()` 94 | 3. `.write()` 95 | 4. `.try_write()` 96 | 97 | 注意需要对 `.try_read()` 和 `.try_write()` 的返回值进行判断。 98 | -------------------------------------------------------------------------------- /quickstart/rust-travel.md: -------------------------------------------------------------------------------- 1 | # Rust旅程 2 | 3 | ## HelloWorld 4 | 按照编程语言的传统,学习第一门编程语言的第一个程序都是打印 Hello World! 5 | 下面根据我们的步骤创建 Rust 的 Hello World!程序: 6 | 7 | **下面的命令操作,如果没有特别说明,都是在shell下运行。本文为了简单统一,所有例子都在 win10 的 powershell 下运行,所有命令都运行在`ps:`标识符之后** 8 | 9 | - 创建一个 Doing 目录和 helloworld.rs 文件 10 | 11 | > ps: mkdir ~/Doing 12 | > ps: cd ~/Doing 13 | > ps: notepad helloworld.rs # 作者偏向于使用 sublime 作为编辑器 14 | > ps: subl helloworld.rs # 本章以后使用 subl 代替 notepad 15 | 16 | 注意这里用的后缀名是.rs,一般编程语言的代码文件都有惯用的后缀名,比如: 17 | C语言是.c,java是.java,python是.py等等,**请务必记住Rust语言的惯用后缀名是.rs**(虽然用别的后缀名也能通过rustc的编译)。 18 | 19 | - 在 helloworld.rs 文件中输入 Rust 代码 20 | 21 | ```rust 22 | fn main() { 23 | println!("Hello World!"); 24 | } 25 | ``` 26 | 27 | - 编译 helloworld.rs 文件 28 | 29 | > ps: rustc helloworld.rs 30 | > ps: rustc helloworld.rs -O # 也可以选择优化编译 31 | 32 | - 运行程序 33 | 34 | > ps: ./helloworld.exe # windows 平台下需要加 .exe 后缀 35 | > Hello World! 36 | 37 | 没有`ps:`前缀的表示为控制台打印输出。 38 | 39 | 我们已经用rust编写第一个可执行程序,打印出了'hello world!',很酷,对吧! 40 | 但是这段代码到底是什么意思呢,作为新手的你一定云里雾里吧,让我们先看一下这个程序: 41 | 42 | 1. 第一行中 fn 表示定义一个**函数**,main是这个函数的名字,花括号{}里的语句则表示这个函数的内容。 43 | 2. 名字叫做**main**的函数有特殊的用途,那就是作为程序的入口,也就是说程序每次都从这个函数开始运行。 44 | 3. 函数中只有一句 ```println!("Hello World!");```,这里```println!```是一个Rust语言自带的**宏**, 45 | 这个宏的功能就是打印文本(结尾会换行),而"Hello World!"这个用引号包起来的东西是一个**字符串**,就是我们要打印的文本。 46 | 4. 你一定注意到了```;```吧, 在Rust语言中,分号```;```用来把语句分隔开,也就是说语句的末尾一般用分号做为结束标志。 47 | 48 | ## HelloRust 49 | 50 | - 创建项目 hellorust 51 | 52 | > ps: cargo new hellorust --bin 53 | 54 | - 查看目录结构 55 | 56 | > ps: tree # win10 powershell 自带有 tree 查看文件目录结构的功能 57 | > └─hellorust 58 | > ----└─src 59 | 60 | 这里显示的目录结构,在hellorust目录下有 src 文件夹和 Cargo.toml 文件,同时这个目录会初始化为 git 项目 61 | 62 | - 查看Cargo.toml文件 63 | 64 | > ps: cat Cargo.toml 65 | > [package] 66 | name = "hellorust" 67 | version = "0.1." 68 | authors = ["YourName "] 69 | [dependencies] 70 | 71 | - 编辑src目录下的main.rs文件 72 | 73 | > ps: subl ./src/main.rs 74 | 75 | cargo 创建的项目,在src目录下会有一个初始化的main.rs文件,内容为: 76 | 77 | ```rust 78 | fn main() { 79 | println!("Hello, world!"); 80 | } 81 | ``` 82 | 83 | 现在我们编辑这个文件,改为: 84 | 85 | ```rust 86 | fn main() { 87 | let rust = "Rust"; 88 | println!("Hello, {}!", rust); 89 | } 90 | ``` 91 | 92 | 这里的 `let rust = "Rust"` 是把 rust 变量绑定为 "Rust" , 93 | `println!("Hello, {}!", rust);`里把 rust 变量的值代入到`"Hello, {}!"`中的`{}`。 94 | 95 | - 编译和运行 96 | 97 | > ps: cargo build 98 | > ps: cargo build --release # 这个属于优化编译 99 | > ps: ./target/debug/hellorust.exe 100 | > ps: ./target/release/hellorust.exe # 如果前面是优化编译,则这样运行 101 | > ps: cargo run # 编译和运行合在一起 102 | > ps: cargo run --release # 同上,区别是是优化编译的 103 | -------------------------------------------------------------------------------- /editors/vim.md: -------------------------------------------------------------------------------- 1 | # vim/GVim安装配置 2 | 3 | 本节介绍vim的Rust支持配置,在阅读本节之前,我们假定你已经拥有了一个可执行的rustc程序,并编译好了racer。 4 | 5 | ## 我的vim截图 6 | 7 | 应邀而加 8 | 9 | ![此处应该有截图](../images/editor-vim-wayslog.png) 10 | 11 | ## 使用vundle 12 | 13 | vundle是vim的一个插件管理工具,基本上算是本类当中最为易用的了。 14 | 首先我们需要安装它 15 | 16 | ### linux or OS X 17 | 18 | ```bash 19 | mkdir -p ~/.vim/bundle/ 20 | git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim 21 | ``` 22 | 23 | ### windows 24 | 25 | 1. 首先找到你的gvim的安装路径,然后在路径下找到vimfiles文件夹 26 | 2. 在这个文件夹中将vundle库克隆到vimfiles/bundle/目录下的Vundle.vim文件夹中 27 | 28 | ## 启用rust支持 29 | 30 | ### 下载源码 31 | 32 | 首先,你需要下载rust-lang的源码文件,并将其解压到一个路径下。 33 | 这个源码文件我们可以从[rust官网](https://www.rust-lang.org/downloads.html)下载到,请下载你对应平台的文件。 34 | 然后将其解压到一个目录下,并找到其源码文件中的`src`目录。 35 | 比如我们解压源码包到`C:\\rust-source\`,那么我们需要的路径就是`C:\\rust-source\src`,记好这个路径,我们将在下一步用到它。 36 | 37 | ### 修改vim配置 38 | 39 | 首先找到你的vimrc配置文件,然后在其中添加如下配置 40 | 41 | ```vim 42 | set nocompatible 43 | filetype off 44 | set rtp+=~/.vim/bundle/Vundle.vim 45 | call vundle#begin() 46 | 47 | Plugin 'VundleVim/Vundle.vim' 48 | Plugin 'racer-rust/vim-racer' 49 | Plugin 'rust-lang/rust.vim' 50 | 51 | call vundle#end() 52 | 53 | filetype on 54 | ``` 55 | 56 | 然后为了让配置生效,我们重启我们的(g)vim,然后在vim里执行如下命令 57 | 58 | ``` 59 | :PluginInstall 60 | ``` 61 | 62 | 这里vundle会自动的去仓库里拉取我们需要的文件,这里主要是vim-racer和rust.vim两个库。 63 | 64 | ### 更多的配置 65 | 66 | 为了让我们的vim能正常的使用,我们还需要在vimrc配置文件里加入一系列配置, 67 | 68 | ```vim 69 | "" 开启rust的自动reformat的功能 70 | let g:rustfmt_autosave = 1 71 | 72 | "" 手动补全和定义跳转 73 | set hidden 74 | "" 这一行指的是你编译出来的racer所在的路径 75 | let g:racer_cmd = "/target/release/racer" 76 | "" 这里填写的就是我们在1.2.1中让你记住的目录 77 | let $RUST_SRC_PATH="/src/" 78 | ``` 79 | 80 | #### 使用 YouCompleteMe 81 | 82 | YouCompleteMe 是 vim 下的智能补全插件, 支持 C-family, Python, Rust 等的语法补全, 整合了多种插件, 功能强大. Linux 各发行版的官方源里基本都有软件包, 可直接安装. 如果有需要进行编译安装的话, 可参考[官方教程](https://github.com/Valloric/YouCompleteMe#installation) 83 | 84 | 让 YCM 支持 Rust 需要在安装 YCM 过程中执行 ./install.py 时加上 --racer-completer, 并在 .vimrc 中添加如下设置 85 | 86 | ``` 87 | let g:ycm_rust_src_path="/src/" 88 | "" 一些方便的快捷键 89 | """ 在 Normal 模式下, 敲 jd 跳转到定义或声明(支持跨文件) 90 | nnoremap jd :YcmCompleter GoToDefinitionElseDeclaration 91 | """ 在 Insert 模式下, 敲 ; 补全 92 | inoremap ; 93 | ``` 94 | 95 | ## 总结 96 | 97 | 经过不多的配置,我们得到了如下功能: 98 | 99 | 1. 基本的c-x c-o补全 (使用 YCM 后, 能做到自动补全) 100 | 2. 语法着色 101 | 3. gd跳转到定义 102 | 103 | 总体来看支持度并不高。 104 | 105 | ![此处应该有第二张截图](../images/editor-vim-welldone.png) 106 | 107 | ### 额外的 108 | Q1. 颜色好挫 109 | 110 | A1. 我推荐一个配色,也是我自己用的 [molokai](https://github.com/tomasr/molokai) 111 | 112 | 更详细内容可以参见我的[vimrc配置](https://github.com/wayslog/dotfiles/blob/master/_vimrc),当然,我这个用的是比较老的版本的vundle,仅供参考。 113 | 114 | Have a nice Rust ! 115 | -------------------------------------------------------------------------------- /ownership-system/borrowing_reference.md: -------------------------------------------------------------------------------- 1 | # 引用&借用(References&Borrowing) 2 | 3 | 4 | 如上所示,Owership让我们改变一个变量的值变得“复杂”,那能否像其他编程语言那样随意改变变量的值呢?答案是有的。 5 | 6 | 所有权系统允许我们通过“Borrowing”的方式达到这个目的。这个机制非常像其他编程语言中的“读写锁”,即同一时刻,只能拥有一个“写锁”,或只能拥有多个“读锁”,不允许“写锁”和“读锁”在同一时刻同时出现。当然这也是数据读写过程中保障一致性的典型做法。只不过Rust是在编译中完成这个(Borrowing)检查的,而不是在运行时,这也就是为什么其他语言程序在运行过程中,容易出现死锁或者野指针的问题。 7 | 8 | 9 | 通过**&**符号完成Borrowing: 10 | 11 | ```rust 12 | fn main() { 13 | let x: Vec = vec!(1i32, 2, 3); 14 | let y = &x; 15 | println!("x={:?}, y={:?}", x, y); 16 | } 17 | ``` 18 | 19 | Borrowing(**&x**)并不会发生所有权moved,所以println可以同时访问x和y。 20 | 通过引用,就可以对普通类型完成修改。 21 | 22 | ```rust 23 | fn main() { 24 | let mut x: i32 = 100; 25 | { 26 | let y: &mut i32 = &mut x; 27 | *y += 2; 28 | } 29 | println!("{}", x); 30 | } 31 | ``` 32 | 33 | ###借用与引用的区别 34 | 35 | 借用与引用是一种相辅相成的关系,若B是对A的引用,也可称之为B借用了A。 36 | 37 | 很相近对吧,但是借用一词本意为要归还。所以在Rust用引用时,一定要注意应该在何处何时正确的“归回”借用/引用。 38 | 最后面的“高级”小节会详细举例。 39 | 40 | ###规则 41 | 42 | 1. 同一作用域,特定数据最多只有一个可变借用(&mut T),或者2。 43 | 2. 同一作用域,特定数据可有0个或多个不可变借用(&T),但不能有任何可变借用。 44 | 3. 借用在离开作用域后释放。 45 | 4. 在可变借用释放前不可访问源变量。 46 | 47 | ###可变性 48 | Borrowing也分“不可变借用”(默认,**&T**)和“可变借用”(**&mut T**)。 49 | 50 | 顾名思义,“不可变借用”是只读的,不可更新被引用的内容。 51 | 52 | ```rust 53 | fn main() { 54 | let x: Vec = vec!(1i32, 2, 3); 55 | 56 | //可同时有多个不可变借用 57 | let y = &x; 58 | let z = &x; 59 | let m = &x; 60 | 61 | //ok 62 | println!("{:?}, {:?}, {:?}, {:?}", x, y, z, m); 63 | } 64 | ``` 65 | 66 | 再次强调下,同一作用域下只能有一个可变借用(&mut T),且被借用的变量本身必须有可变性 : 67 | 68 | ```rust 69 | fn main() { 70 | //源变量x可变性 71 | let mut x: Vec = vec!(1i32, 2, 3); 72 | 73 | //只能有一个可变借用 74 | let y = &mut x; 75 | // let z = &mut x; //错误 76 | y.push(100); 77 | 78 | //ok 79 | println!("{:?}", y); 80 | 81 | //错误,可变借用未释放,源变量不可访问 82 | // println!("{:?}", x); 83 | } //y在此处销毁 84 | ``` 85 | 86 | ###高级例子 87 | 下面的复杂例子,进行了详细的注释,即使看不懂也没关系,可以在完成Lifetimes(生命周期)的学习后再仔细思考本例子。 88 | 89 | ```rust 90 | fn main() { 91 | let mut x: Vec = vec!(1i32, 2, 3); 92 | 93 | //更新数组 94 | //push中对数组进行了可变借用,并在push函数退出时销毁这个借用 95 | x.push(10); 96 | 97 | { 98 | //可变借用1 99 | let mut y = &mut x; 100 | y.push(100); 101 | 102 | //可变借用2,注意:此处是对y的借用,不可再对x进行借用, 103 | //因为y在此时依然存活。 104 | let z = &mut y; 105 | z.push(1000); 106 | 107 | println!("{:?}", z); //打印: [1, 2, 3, 10, 100, 1000] 108 | } //y和z在此处被销毁,并释放借用。 109 | 110 | 111 | //访问x正常 112 | println!("{:?}", x); //打印: [1, 2, 3, 10, 100, 1000] 113 | } 114 | ``` 115 | 116 | ####总结 117 | 1. 借用不改变内存的所有者(Owner),借用只是对源内存的临时引用。 118 | 2. 在借用周期内,借用方可以读写这块内存,所有者被禁止读写内存;且所有者保证在有“借用”存在的情况下,不会释放或转移内存。 119 | 3. 失去所有权的变量不可以被借用(访问)。 120 | 4. 在租借期内,内存所有者保证不会释放/转移/可变租借这块内存,但如果是在**非可变租借**的情况下,所有者是允许继续**非可变租借**出去的。 121 | 5. 借用周期满后,所有者收回读写权限 122 | 6. 借用周期小于被借用者(所有者)的生命周期。 123 | 124 | > 备注: 125 | >   借用周期,指的是借用的有效时间段。 126 | -------------------------------------------------------------------------------- /1st-glance/README.md: -------------------------------------------------------------------------------- 1 | Rust 是一门系统级编程语言,被设计为保证内存和线程安全,并防止段错误。作为系统级编程语言,它的基本理念是 “零开销抽象”。理论上来说,它的速度与 C / C++ 同级。 2 | 3 | Rust 可以被归为通用的、多范式、编译型的编程语言,类似 C 或者 C++。与这两门编程语言不同的是,Rust 是线程安全的! 4 | 5 | Rust 编程语言的目标是,创建一个高度安全和并发的软件系统。它强调安全性、并发和内存控制。尽管 Rust 借用了 C 和 C++ 的语法,它不允许空指针和悬挂指针,二者是 C 和 C++ 中系统崩溃、内存泄露和不安全代码的根源。 6 | 7 | Rust 中有诸如 if else 和循环语句 for 和 while 的通用控制结构。和 C 和 C++ 风格的编程语言一样,代码段放在花括号中。 8 | 9 | Rust 使用实现(implementation)、特征(trait)和结构化类型(structured type)而不是类(class)。这点,与基于继承的OO语言 C++, Java 有相当大的差异。而跟 Ocaml, Haskell 这类函数式语言更加接近。 10 | 11 | Rust 做到了内存安全而无需 .NET 和 Java 编程语言中实现自动垃圾收集器的开销,这是通过所有权/借用机制、生命周期、以及类型系统来达到的。 12 | 13 | 下面是一个代码片段的例子,经典的 Hello World 应用: 14 | 15 | ``` rust 16 | fn main() { 17 | println!("hello, world"); 18 | } 19 | ``` 20 | 21 | 影响了 Rust 的流行的编程语言包括 C, C++, C#, Erlang, Haskell, OCaml, Ruby, Scheme 和 Swift 等等。Rust 也影响了 C# 7, Elm, Idris, Swift。 22 | 23 | Rust 提供了安装程序,你只需要从官网下载并在相应的操作系统上运行安装程序。安装程序支持 Windows、Mac 和 Linux(通过脚本)上的32位和64位 CPU 体系架构,适用 Apache License 2.0 或者 MIT Licenses。 24 | 25 | Rust 运行在以下操作系统上:Linux, OS X, Windows, FreeBSD, Android, iOS。 26 | 27 | 简单提一下 Rust 的历史。Rust 最早是 Mozilla 雇员 Graydon Hoare 的一个个人项目,从 2009 年开始,得到了 Mozilla 研究院的支助,2010 年项目对外公布。2010 ~2011 年间实现的自举。从此以后,Rust 经历了巨大的设计变化和反复(历程极其艰辛),终于在 2015 年 5 月 15日发布了 1.0 版。在这个研发过程中,Rust 建立了一个强大活跃的社区,形成了一整套完善稳定的项目贡献机制(这是真正的可怕之处)。Rust 现在由 Rust 项目开发者社区(https://github.com/rust-lang/rust )维护。 28 | 29 | 自 15 年 5 月 1.0 发布以来,涌现了大量优秀的项目(可以 github 上搜索 Rust 查找),大公司也逐渐积极参与 Rust 的应用开发,以及回馈开源社区。 30 | 31 | 本书(RustPrimer)旨在为中文 Rustaceans 初学者提供一个正确、最新、易懂的中文教程。本书会一直完善跟进,永不停歇。 32 | 33 | 本书是整个 Rust 中文社区共同努力的结果。其中,参与本书书写及校订的 Rustacean 有(排名不分先后): 34 | 35 | - [daogangtang(Mike猫)](https://github.com/daogangtang) 36 | - [wayslog(猫猫反抗团团长)](https://github.com/wayslog) 37 | - [marvin-min](https://github.com/marvin-min) 38 | - [tiansiyuan](https://github.com/tiansiyuan) 39 | - [marvinguo](https://github.com/marvinguo) 40 | - ee0703 41 | - fuyingfuying 42 | - qdao 43 | - JohnSmithX 44 | - [stormgbs (AX) ](https://github.com/stormgbs) 45 | - tennix 46 | - anzhihun 47 | - zonyitoo(Elton, e猫) 48 | - 42 49 | - [Naupio(N猫)](https://github.com/Naupio) 50 | - F001(失落的神喵) 51 | - wangyu190810 52 | - domty 53 | - [MarisaKirisame(帅气可爱魔理沙)](https://github.com/MarisaKirisame) 54 | - [Liqueur Librazy](https://github.com/Librazy) 55 | - [Knight42](https://github.com/knight42) 56 | - [Ryan Kung](https://github.com/ryankung) 57 | - lambdaplus 58 | - doomsplayer 59 | - lucklove 60 | - veekxt 61 | - lk-chen 62 | - RyanKung 63 | - arrowrowe 64 | - marvin-min 65 | - ghKelo 66 | - wy193777 67 | - domty 68 | - xusss 69 | - wangyu190810 70 | - nextzhou 71 | - zhongke 72 | - [ryuki](https://github.com/3442853561) 73 | - codeworm96 74 | - anzhihun 75 | - lidashuang 76 | - sceext2 77 | - loggerhead 78 | - twq0076262 79 | - passchaos 80 | - yyrust 81 | - markgeek 82 | - ts25504 83 | - overvenus 84 | - Akagi201 85 | - theJian 86 | - jqs7 87 | - ahjdzx 88 | - chareice 89 | - chenshaobo 90 | - marvinguo 91 | - izgzhen 92 | - ziqin 93 | - peng1999 94 | 95 | 等。在此,向他们的辛苦工作和无私奉献表示尊敬和感谢! 96 | 97 | 祝用 Rust 编程愉快! 98 | -------------------------------------------------------------------------------- /quickstart/struct-enum.md: -------------------------------------------------------------------------------- 1 | # 结构体与枚举 2 | 3 | ## 结构体 4 | 5 | 结构体 (struct) 是一种记录类型,所包含的每个域 (field) 都有一个名称。 6 | 每个结构体也都有一个名称,通常以大写字母开头,使用驼峰命名法。 7 | 元组结构体 (tuple struct) 是由元组和结构体混合构成,元组结构体有名称, 8 | 但是它的域没有。当元组结构体只有一个域时,称为新类型 (newtype)。 9 | 没有任何域的结构体,称为类单元结构体 (unit-like struct)。 10 | 结构体中的值默认是不可变的,需要给结构体加上`mut`使其可变。 11 | 12 | ```rust 13 | // structs 14 | struct Point { 15 | x: i32, 16 | y: i32, 17 | } 18 | let point = Point { x: 0, y: 0 }; 19 | 20 | // tuple structs 21 | struct Color(u8, u8, u8); 22 | let android_green = Color(0xa4, 0xc6, 0x39); 23 | let Color(red, green, blue) = android_green; 24 | 25 | // A tuple struct’s constructors can be used as functions. 26 | struct Digit(i32); 27 | let v = vec![0, 1, 2]; 28 | let d: Vec = v.into_iter().map(Digit).collect(); 29 | 30 | // newtype: a tuple struct with only one element 31 | struct Inches(i32); 32 | let length = Inches(10); 33 | let Inches(integer_length) = length; 34 | 35 | // unit-like structs 36 | struct EmptyStruct; 37 | let empty = EmptyStruct; 38 | ``` 39 | 40 | 一个包含`..`的`struct`可以用来从其它结构体拷贝一些值或者在解构时忽略一些域: 41 | 42 | ```rust 43 | #[derive(Default)] 44 | struct Point3d { 45 | x: i32, 46 | y: i32, 47 | z: i32, 48 | } 49 | 50 | let origin = Point3d::default(); 51 | let point = Point3d { y: 1, ..origin }; 52 | let Point3d { x: x0, y: y0, .. } = point; 53 | ``` 54 | 55 | 需要注意,Rust在语言级别不支持域可变性 (field mutability),所以不能这么写: 56 | 57 | ```rust 58 | struct Point { 59 | mut x: i32, 60 | y: i32, 61 | } 62 | ``` 63 | 64 | 这是因为可变性是绑定的一个属性,而不是结构体自身的。可以使用`Cell`来模拟: 65 | 66 | ```rust 67 | use std::cell::Cell; 68 | 69 | struct Point { 70 | x: i32, 71 | y: Cell, 72 | } 73 | 74 | let point = Point { x: 5, y: Cell::new(6) }; 75 | 76 | point.y.set(7); 77 | ``` 78 | 79 | 此外,结构体的域对其所在模块 (mod) 之外默认是私有的,可以使用`pub`关键字将其设置成公开。 80 | 81 | ```rust 82 | mod graph { 83 | #[derive(Default)] 84 | pub struct Point { 85 | pub x: i32, 86 | y: i32, 87 | } 88 | 89 | pub fn inside_fn() { 90 | let p = Point {x:1, y:2}; 91 | println!("{}, {}", p.x, p.y); 92 | } 93 | } 94 | 95 | fn outside_fn() { 96 | let p = graph::Point::default(); 97 | println!("{}", p.x); 98 | // println!("{}", p.y); 99 | // field `y` of struct `graph::Point` is private 100 | } 101 | ``` 102 | 103 | ## 枚举 104 | Rust有一个集合类型,称为枚举 (enum),代表一系列子数据类型的集合。 105 | 其中子数据结构可以为空-如果全部子数据结构都是空的,就等价于C语言中的enum。 106 | 我们需要使用`::`来获得每个元素的名称。 107 | 108 | ```rust 109 | // enums 110 | enum Message { 111 | Quit, 112 | ChangeColor(i32, i32, i32), 113 | Move { x: i32, y: i32 }, 114 | Write(String), 115 | } 116 | 117 | let x: Message = Message::Move { x: 3, y: 4 }; 118 | ``` 119 | 120 | 与结构体一样,枚举中的元素默认不能使用关系运算符进行比较 (如`==`, `!=`, `>=`), 121 | 也不支持像`+`和`*`这样的双目运算符,需要自己实现,或者使用`match`进行匹配。 122 | 123 | 枚举默认也是私有的,如果使用`pub`使其变为公有,则它的元素也都是默认公有的。 124 | 这一点是与结构体不同的:即使结构体是公有的,它的域仍然是默认私有的。这里的共有/私有仍然 125 | 是针对其定义所在的模块之外。此外,枚举和结构体也可以是递归的 (recursive)。 126 | -------------------------------------------------------------------------------- /ffi/compiling-rust-to-lib.md: -------------------------------------------------------------------------------- 1 | # 将Rust编译成库 2 | 上一章讲述了如何从rust中调用c库,这一章我们讲如何把rust编译成库让别的语言通过cffi调用。 3 | 4 | ## 调用约定和mangle 5 | 正如上一章讲述的,为了能让rust的函数通过ffi被调用,需要加上`extern "C"`对函数进行修饰。 6 | 7 | 但由于rust支持重载,所以函数名会被编译器进行混淆,就像c++一样。因此当你的函数被编译完毕后,函数名会带上一串表明函数签名的字符串。 8 | 9 | 比如:`fn test() {}`会变成`_ZN4test20hf06ae59e934e5641haaE`. 10 | 这样的函数名为ffi调用带来了困难,因此,rust提供了`#[no_mangle]`属性为函数修饰。 11 | 对于带有`#[no_mangle]`属性的函数,rust编译器不会为它进行函数名混淆。如: 12 | 13 | ```rust 14 | #[no_mangle] 15 | extern "C" fn test() {} 16 | ``` 17 | 18 | 在nm中观察到为 19 | 20 | ``` 21 | ... 22 | 00000000001a7820 T test 23 | ... 24 | ``` 25 | 26 | 至此,`test`函数将能够被正常的由`cffi`调用。 27 | ## 指定`crate`类型 28 | `rustc`默认编译产生`rust`自用的`rlib`格式库,要让`rustc`产生动态链接库或者静态链接库,需要显式指定。 29 | 30 | 1. 方法1: 在文件中指定。 31 | 在文件头加上`#![crate_type = "foo"]`, 其中`foo`的可选类型有`bin`, `lib`, `rlib`, `dylib`, `staticlib`.分别对应可执行文件, 32 | 默认(将由`rustc`自己决定), `rlib`格式,动态链接库,静态链接库。 33 | 2. 方法2: 编译时给rustc 传`--crate-type`参数。参数内容同上。 34 | 3. 方法3: 使用cargo,指定`crate-type = ["foo"] `, `foo`可选类型同1 35 | 36 | ## 小技巧: `Any` 37 | 38 | 由于在跨越`ffi`过程中,`rust`类型信息会丢失,比如当用`rust`提供一个`OpaqueStruct`给别的语言时: 39 | 40 | ```rust 41 | use std::mem::transmute; 42 | 43 | #[derive(Debug)] 44 | struct Foo { 45 | t: T 46 | } 47 | 48 | #[no_mangle] 49 | extern "C" fn new_foo_vec() -> *const c_void { 50 | Box::into_raw(Box::new(Foo {t: vec![1,2,3]})) as *const c_void 51 | } 52 | 53 | #[no_mangle] 54 | extern "C" fn new_foo_int() -> *const c_void { 55 | Box::into_raw(Box::new(Foo {t: 1})) as *const c_void 56 | } 57 | 58 | fn push_foo_element(t: &mut Foo>) { 59 | t.t.push(1); 60 | } 61 | 62 | #[no_mangle] 63 | extern "C" fn push_foo_element_c(foo: *mut c_void){ 64 | let foo2 = unsafe { 65 | &mut *(foo as *mut Foo>) // 这么确定是Foo>? 万一foo是Foo怎么办? 66 | }; 67 | push_foo_element(foo3); 68 | } 69 | ``` 70 | 71 | 以上代码中完全不知道`foo`是一个什么东西。安全也无从说起了,只能靠文档。 72 | 因此在`ffi`调用时往往会丧失掉`rust`类型系统带来的方便和安全。在这里提供一个小技巧:使用`Box>`来包装你的类型。 73 | 74 | `rust`的`Any`类型为`rust`带来了运行时反射的能力,使用`Any`跨越`ffi`边界将极大提高程序安全性。 75 | 76 | ```rust 77 | use std::any::Any; 78 | 79 | #[derive(Debug)] 80 | struct Foo { 81 | t: T 82 | } 83 | 84 | #[no_mangle] 85 | extern "C" fn new_foo_vec() -> *const c_void { 86 | Box::into_raw(Box::new(Box::new(Foo {t: vec![1,2,3]}) as Box)) as *const c_void 87 | } 88 | 89 | #[no_mangle] 90 | extern "C" fn new_foo_int() -> *const c_void { 91 | Box::into_raw(Box::new(Box::new(Foo {t: 1}) as Box)) as *const c_void 92 | } 93 | 94 | fn push_foo_element(t: &mut Foo>) { 95 | t.t.push(1); 96 | } 97 | 98 | #[no_mangle] 99 | extern "C" fn push_foo_element_c(foo: *mut c_void){ 100 | let foo2 = unsafe { 101 | &mut *(foo as *mut Box) 102 | }; 103 | let foo3: Option<&mut Foo>> = foo2.downcast_mut(); // 如果foo2不是*const Box>>, 则foo3将会是None 104 | if let Some(value) = foo3 { 105 | push_foo_element(value); 106 | } 107 | } 108 | ``` 109 | 110 | 这样一来,就非常不容易出错了。 111 | -------------------------------------------------------------------------------- /type/string.md: -------------------------------------------------------------------------------- 1 | # String 2 | 3 | 这章我们来着重介绍一下字符串。 4 | 5 | 刚刚学习Rust的同学可能会被Rust的字符串搞混掉,比如`str`,`String`, `OsStr`, `CStr`,`CString`等等…… 6 | 事实上,如果你不做FFI的话,常用的字符串类型就只有前两种。我们就来着重研究一下Rust的前两种字符串。 7 | 8 | 你要明白的是,Rust中的字符串实际上是被编码成UTF-8的一个字节数组。这么说比较拗口,简单来说,Rust字符串内部存储的是一个u8数组,但是这个数组是Unicode字符经过UTF-8编码得来的。因此,可以看成Rust原生就支持Unicode字符集(Python2的码农泪流满面)。 9 | 10 | ## str 11 | 12 | 首先我们先来看一下`str`, 从字面意思上,Rust的string被表达为: `&'static str`(看不懂这个表达式没关系,&表示引用你知道吧,static表示静态你知道吧,好了,齐了),即,你在代码里写的,所有的用`""`包裹起来的字符串,都被声明成了一个不可变,静态的字符串。而我们的如下语句: 13 | 14 | ```rust 15 | let x = "Hello"; 16 | let x:&'static str = "Hello"; 17 | ``` 18 | 19 | 实际上是将 `"Hello"` 这个静态变量的引用传递给了`x`。同时,这里的字符串不可变! 20 | 21 | 字符串也支持转义字符: 22 | 比如如下: 23 | 24 | ```rust 25 | let z = "foo 26 | bar"; 27 | let w = "foo\nbar"; 28 | assert_eq!(z, w); 29 | ``` 30 | 31 | 也可以在字符串字面量前加上`r`来避免转义 32 | 33 | //没有转义序列 34 | let d: &'static str = r"abc \n abc"; 35 | //等价于 36 | let c: &'static str = "abc \\n abc"; 37 | 38 | ## String 39 | 40 | 光有`str`,确实不够什么卵用,毕竟我们在实际应用中要的更多的还是一个可变的,不定长的字符串。这时候,一种在堆上声明的字符串`String`被设计了出来。 41 | 它能动态的去增长或者缩减,那么怎么声明它呢?我们先介绍一种简单的方式,从`str`中转换: 42 | 43 | ```rust 44 | let x:&'static str = "hello"; 45 | 46 | let mut y:String = x.to_string(); 47 | println!("{}", y); 48 | y.push_str(", world"); 49 | println!("{}", y); 50 | ``` 51 | 52 | 我知道你一定会问:—— 53 | 那么如何将一个`String`重新变成`&str`呢? 54 | 答:用 `&*` 符号 55 | 56 | ```rust 57 | fn use_str(s: &str) { 58 | println!("I am: {}", s); 59 | } 60 | 61 | fn main() { 62 | let s = "Hello".to_string(); 63 | use_str(&*s); 64 | } 65 | ``` 66 | 67 | 我们来分析一下,以下部分将涉及到部分`Deref`的知识,可能需要你预习一下,如果不能理解大可跳过下一段: 68 | 69 | 首先呢, `&*`是两个符号`&`和`*`的组合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。 70 | 71 | 由于`String`实现了 `impl Deref for String`,这相当于一个运算符重载,所以你就能通过`*`获得一个`str`类型。但是我们知道,单独的`str`是不能在Rust里直接存在的,因此,我们需要先给他进行`&`操作取得`&str`这个结果。 72 | 73 | 有人说了,我发现只要用`&`一个操作符就能将使上面的编译通过。 74 | 这其实是一个编译器的锅,因为Rust的编译器会在`&`后面插入足够多的`*`来尽可能满足`Deref`这个特性。这个特性会在某些情况下失效,因此,为了不给自己找麻烦,还是将操作符写全为好。 75 | 76 | 77 | 需要知道的是,将`String`转换成`&str`是非常轻松的,几乎没有任何开销。但是反过来,将`&str`转换成`String`是需要在堆上请求内存的,因此,要慎重。 78 | 79 | 我们还可以将一个UTF-8编码的字节数组转换成String,如 80 | 81 | ```rust 82 | // 存储在Vec里的一些字节 83 | let miao = vec![229,150,181]; 84 | 85 | // 我们知道这些字节是合法的UTF-8编码字符串,所以直接unwrap() 86 | let meow = String::from_utf8(miao).unwrap(); 87 | 88 | assert_eq!("喵", meow); 89 | ``` 90 | 91 | ## 索引访问 92 | 93 | 有人会把Rust中的字符串和其惯用的字符串等同起来,于是就出现了如下代码 94 | 95 | ```rust 96 | let x = "hello".to_string(); 97 | x[1]; //编译错误! 98 | ``` 99 | 100 | Rust的字符串实际上是不支持通过下标访问的,但是呢,我们可以通过将其转变成数组的方式访问 101 | 102 | ```rust 103 | let x = "哎哟我去".to_string(); 104 | for i in x.as_bytes() { 105 | print!("{} ", i); 106 | } 107 | 108 | println!(""); 109 | 110 | for i in x.chars() { 111 | print!("{}", i); 112 | } 113 | 114 | x.chars().nth(2); 115 | ``` 116 | 117 | ## 字符串切片 118 | 119 | 对字符串切片是一件非常危险的事,虽然Rust支持,但是我并不推荐。因为Rust的字符串Slice实际上是切的bytes。这也就造成了一个严重后果,如果你切片的位置正好是一个Unicode字符的内部,Rust会发生Runtime的panic,导致整个程序崩溃。 120 | 因为这个操作是如此的危险,所以我就不演示了…… 121 | -------------------------------------------------------------------------------- /collections/hashmap.md: -------------------------------------------------------------------------------- 1 | # 哈希表 HashMap 2 | 3 | 和动态数组`Vec`一样,哈希表(HashMap)也是Rust内置的集合类型之一,同属`std::collections`模块下。 4 | 5 | 它提供了一个平均复杂度为`O(1)`的查询方法,是实现快速搜索必备的类型之一。 6 | 7 | 这里呢,主要给大家介绍一下HashMap的几种典型用法。 8 | 9 | ## HashMap的要求 10 | 11 | 顾名思义, HashMap 要求一个可哈希(实现 Hash trait)的Key类型,和一个编译时知道大小的Value类型。 12 | 同时,Rust还要求你的Key类型必须是可比较的,在Rust中,你可以为你的类型轻易的加上编译器属性: 13 | 14 | ```rust 15 | #[derive(PartialEq, Eq, Hash)] 16 | ``` 17 | 18 | 这样,即可将你的类型转换成一个可以作为Hash的Key的类型。 19 | 但是,如果你想要自己实现`Hash`这个trait的话,你需要谨记两点: 20 | 21 | * 1. 如果 Key1==Key2 ,那么一定有 Hash(Key1) == Hash(Key2) 22 | * 2. 你的Hash函数本身不能改变你的Key值,否则将会引发一个逻辑错误(很难排查,遇到就完的那种) 23 | 24 | 什么?你看到 `std::hash::Hash` 这个 trait 中的函数没有`&mut self`的啊!但是,你不要忘了Rust中还有`Cell`和`RefCell`这种存在,他们提供了不可变对象的内部可变性,具体怎么变呢,请参照第20章。 25 | 26 | 另外,要保证你写的Hash函数不会被很轻易的碰撞,即 `Key1! = Key2`,但 `Hash(Key1)==Hash(Key2)`,碰撞的严重了,HashMap甚至有可能退化成链表! 27 | 28 | 这里笔者提议,别费劲,就按最简单的来就好。 29 | 30 | ## 增删改查 31 | 32 | 对于这种实用的类型,我们推荐用一个例子来解释: 33 | 34 | ```rust 35 | use std::collections::HashMap; 36 | 37 | // 声明 38 | let mut come_from = HashMap::new(); 39 | // 插入 40 | come_from.insert("WaySLOG", "HeBei"); 41 | come_from.insert("Marisa", "U.S."); 42 | come_from.insert("Mike", "HuoGuo"); 43 | 44 | // 查找key 45 | if !come_from.contains_key("elton") { 46 | println!("Oh, 我们查到了{}个人,但是可怜的Elton猫还是无家可归", come_from.len()); 47 | } 48 | 49 | // 根据key删除元素 50 | come_from.remove("Mike"); 51 | println!("Mike猫的家乡不是火锅!不是火锅!不是火锅!虽然好吃!"); 52 | 53 | // 利用get的返回判断元素是否存在 54 | let who = ["MoGu", "Marisa"]; 55 | for person in &who { 56 | match come_from.get(person) { 57 | Some(location) => println!("{} 来自: {}", person, location), 58 | None => println!("{} 也无家可归啊.", person), 59 | } 60 | } 61 | 62 | // 遍历输出 63 | println!("那么,所有人呢?"); 64 | for (name, location) in &come_from { 65 | println!("{}来自: {}", name, location); 66 | } 67 | ``` 68 | 69 | 这段代码输出: 70 | 71 | ``` 72 | Oh, 我们查到了3个人,但是可怜的Elton猫还是无家可归 73 | Mike猫的家乡不是火锅!不是火锅!不是火锅!虽然好吃! 74 | MoGu 也无家可归啊. 75 | Marisa 来自: U.S. 76 | 那么,所有人呢? 77 | Marisa来自: U.S. 78 | WaySLOG来自: HeBei 79 | ``` 80 | 81 | ## entry 82 | 83 | 我们在编程的过程中,经常遇到这样的场景,统计一个字符串中所有的字符总共出现过几次。借助各种语言内置的Map类型我们总能完成这件事,但是完成的几乎都并不令人满意。很多人讨厌的一点是:为什么我要判断这个字符在字典中有没有出现,就要写一个大大的if条件!烦不烦?烦!于是,现代化的编程语言开始集成了类似Python里`setdefault`类似的特性(方法),下面是一段Python代码: 84 | 85 | ```python 86 | val = {} 87 | for c in "abcdefasdasdawe": 88 | val[c] = 1 + val.setdefault(c, 0) 89 | print val 90 | ``` 91 | 92 | 唔,总感觉怪怪的。那么Rust是怎么解决这个问题的呢? 93 | 以下内容摘自标注库api注释: 94 | 95 | ```rust 96 | use std::collections::HashMap; 97 | 98 | let mut letters = HashMap::new(); 99 | 100 | for ch in "a short treatise on fungi".chars() { 101 | let counter = letters.entry(ch).or_insert(0); 102 | *counter += 1; 103 | } 104 | 105 | assert_eq!(letters[&'s'], 2); 106 | assert_eq!(letters[&'t'], 3); 107 | assert_eq!(letters[&'u'], 1); 108 | assert_eq!(letters.get(&'y'), None); 109 | ``` 110 | 111 | Rust为我们提供了一个名叫 `entry` 的api,它很有意思,和Python相比,我们不需要在一次迭代的时候二次访问原map,只需要借用 entry 出来的Entry类型(这个类型持有原有HashMap的引用)即可对原数据进行修改。就语法来说,毫无疑问Rust在这个方面更加直观和具体。 112 | -------------------------------------------------------------------------------- /editors/emacs.md: -------------------------------------------------------------------------------- 1 | # Emacs 2 | 3 | 本节介绍 Emacs (Version 24) 的 Rust 配置,假设你已经安装好了 Emacs,并且有使用 Emacs 的经验。具体的安装和使用说明,见网上相关文档,在此不赘述。 4 | 5 | 另外,本节的例子是在 Mac OS 上,在 Linux 上面基本一样。 6 | 7 | Windows的Emacs用户仅作参考。 8 | 9 | ## 简介 10 | 11 | Emacs 的 rust-mode 提供了语法高亮显示和 elisp 函数,可以围绕 Rust 函数定义移动光标。有几个插件提供了附加的功能,如自动补全和动态语法检查。 12 | 13 | ![](../images/editor-emacs-base.png) 14 | 15 | ## 安装插件 16 | 17 | 首先,需要将 melpa 代码库添加到你的插件列表中,才能安装 Rust 需要的插件。将下面的代码片段加入你的```~/.emacs.d/init.el``` 文件中。 18 | 19 | ``` 20 | ;; Add melpa repository to archives 21 | (add-to-list 'package-archives 22 | '("melpa" . "http://melpa.milkbox.net/packages/") t) 23 | 24 | ;; Initialize packages 25 | (package-initialize) 26 | 27 | ``` 28 | 29 | 运行下面的命令,更新插件列表。 30 | 31 | - M-x eval-buffer 32 | - M-x package-refresh-contents 33 | 34 | 然后,就可以安装插件,在 Emacs 中使用 Rust 了。运行 ```M-x package-list-packages```,用 ```i``` 标记下述插件进行安装,当所有的插件选择好了之后,用 ```x``` 执行安装。 35 | 36 | - company 37 | - company-racer 38 | - racer 39 | - flycheck 40 | - flycheck-rust 41 | - rust-mode 42 | 43 | 将下面的代码片段加入你的```~/.emacs.d/init.el``` 文件: 44 | 45 | ``` 46 | ;; Enable company globally for all mode 47 | (global-company-mode) 48 | 49 | ;; Reduce the time after which the company auto completion popup opens 50 | (setq company-idle-delay 0.2) 51 | 52 | ;; Reduce the number of characters before company kicks in 53 | (setq company-minimum-prefix-length 1) 54 | ;; Set path to racer binary 55 | (setq racer-cmd "/usr/local/bin/racer") 56 | 57 | ;; Set path to rust src directory 58 | (setq racer-rust-src-path "/Users/YOURUSERNAME/.rust/src/") 59 | 60 | ;; Load rust-mode when you open `.rs` files 61 | (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) 62 | 63 | ;; Setting up configurations when you load rust-mode 64 | (add-hook 'rust-mode-hook 65 | 66 | '(lambda () 67 | ;; Enable racer 68 | (racer-activate) 69 | 70 | ;; Hook in racer with eldoc to provide documentation 71 | (racer-turn-on-eldoc) 72 | 73 | ;; Use flycheck-rust in rust-mode 74 | (add-hook 'flycheck-mode-hook #'flycheck-rust-setup) 75 | 76 | ;; Use company-racer in rust mode 77 | (set (make-local-variable 'company-backends) '(company-racer)) 78 | 79 | ;; Key binding to jump to method definition 80 | (local-set-key (kbd "M-.") #'racer-find-definition) 81 | 82 | ;; Key binding to auto complete and indent 83 | (local-set-key (kbd "TAB") #'racer-complete-or-indent))) 84 | 85 | ``` 86 | 87 | ![](../images/editor-emacs-error-checking.png) 88 | 89 | ## 配置 Racer 90 | 91 | Racer 需要 Rust 的源代码用于自动补全。 92 | 93 | - git clone https://github.com/rust-lang/rust.git ~/.rust 94 | - 重新启动 Emacs 并打开一个 Rust 源代码文件。 95 | 96 | ![](../images/editor-emacs-completion.png) 97 | 98 | ## 结论 99 | 100 | 现在,可以在 Emacs 中编辑 Rust 源代码文件了。功能总结如下: 101 | 102 | - 语法高亮显示和自动缩进 103 | - 自动补全 104 | - 动态语法错误检查 105 | - 跳转到函数定义 106 | - 内嵌文档 107 | 108 | ![](../images/editor-emacs-jump.gif) 109 | 110 | ## 注释 111 | 112 | 1. 本节的内容适用于 Emacs Version 24;版本 23 的配置方法不同;版本 22 及以下不支持。 113 | 2. MacOS 自带的 Emacs 版本是 22,版本 24 可以从[这里](http://emacsformacosx.com/)下载。 114 | -------------------------------------------------------------------------------- /operator-overloading/operator.md: -------------------------------------------------------------------------------- 1 | # 运算符重载 2 | 3 | Rust可以让我们对某些运算符进行重载,这其中大部分的重载都是对`std::ops`下的trait进行重载而实现的。 4 | 5 | ## 重载加法 6 | 7 | 我们现在来实现一个只支持加法的阉割版[复数](https://zh.wikipedia.org/wiki/%E5%A4%8D%E6%95%B0_%28%E6%95%B0%E5%AD%A6%29): 8 | 9 | ```rust 10 | use std::ops::Add; 11 | 12 | #[derive(Debug)] 13 | struct Complex { 14 | a: f64, 15 | b: f64, 16 | } 17 | 18 | impl Add for Complex { 19 | type Output = Complex; 20 | fn add(self, other: Complex) -> Complex { 21 | Complex {a: self.a+other.a, b: self.b+other.b} 22 | } 23 | } 24 | 25 | fn main() { 26 | let cp1 = Complex{a: 1f64, b: 2.0}; 27 | let cp2 = Complex{a: 5.0, b:8.1}; 28 | let cp3 = cp1 + cp2; 29 | print!("{:?}", cp3); 30 | } 31 | ``` 32 | 33 | 输出: 34 | 35 | ``` 36 | Complex { a: 6, b: 10.1} 37 | ``` 38 | 39 | 这里我们实现了`std::ops::Add`这个trait。这时候有同学一拍脑门,原来如此,没错……其实Rust的大部分运算符都是`std::ops`下的trait的语法糖! 40 | 41 | 我们来看看`std::ops::Add`的具体结构 42 | 43 | ```rust 44 | impl Add for Point { 45 | type Output = f64; 46 | 47 | fn add(self, rhs: i32) -> f64 { 48 | // add an i32 to a Point and get an f64 49 | } 50 | } 51 | ``` 52 | 53 | ## 神奇的Output以及动态分发 54 | 有的同学会问了,这个`Output`是肿么回事?答,类型转换哟亲! 55 | 举个不太恰当的栗子,我们在现实中会出现`0.5+0.5=1`这样的算式,用Rust的语言来描述就是: 两个`f32`相加得到了一个`i8`。显而易见,Output就是为这种情况设计的。 56 | 57 | 还是看代码: 58 | 59 | ```rust 60 | use std::ops::Add; 61 | 62 | #[derive(Debug)] 63 | struct Complex { 64 | a: f64, 65 | b: f64, 66 | } 67 | 68 | impl Add for Complex { 69 | type Output = Complex; 70 | fn add(self, other: Complex) -> Complex { 71 | Complex {a: self.a+other.a, b: self.b+other.b} 72 | } 73 | } 74 | 75 | impl Add for Complex { 76 | type Output = f64; 77 | fn add(self, other: i32) -> f64 { 78 | self.a + self.b + (other as f64) 79 | } 80 | } 81 | 82 | fn main() { 83 | let cp1 = Complex{a: 1f64, b: 2.0}; 84 | let cp2 = Complex{a: 5.0, b:8.1}; 85 | let cp3 = Complex{a: 9.0, b:20.0}; 86 | let complex_add_result = cp1 + cp2; 87 | print!("{:?}\n", complex_add_result); 88 | print!("{:?}", cp3 + 10i32); 89 | } 90 | ``` 91 | 92 | 输出结果: 93 | 94 | ``` 95 | Complex { a: 6, b: 10.1 } 96 | 39 97 | ``` 98 | 99 | ## 对范型的限制 100 | 101 | Rust的运算符是基于trait系统的,同样的,运算符可以被当成一种对范型的限制,我们可以这么要求`范型T必须实现了trait Mul`。 102 | 于是,我们得到了如下的一份代码: 103 | 104 | ```rust 105 | use std::ops::Mul; 106 | 107 | trait HasArea { 108 | fn area(&self) -> T; 109 | } 110 | 111 | struct Square { 112 | x: T, 113 | y: T, 114 | side: T, 115 | } 116 | 117 | impl HasArea for Square 118 | where T: Mul + Copy { 119 | fn area(&self) -> T { 120 | self.side * self.side 121 | } 122 | } 123 | 124 | fn main() { 125 | let s = Square { 126 | x: 0.0f64, 127 | y: 0.0f64, 128 | side: 12.0f64, 129 | }; 130 | 131 | println!("Area of s: {}", s.area()); 132 | } 133 | ``` 134 | 135 | 对于trait `HasArea`和 struct `Square`,我们通过`where T: Mul + Copy` 限制了`T`必须实现乘法。同时Copy则限制了Rust不再将self.side给move到返回值里去。 136 | 137 | 写法简单,轻松愉快。 138 | -------------------------------------------------------------------------------- /std/net.md: -------------------------------------------------------------------------------- 1 | # 网络模块:W猫的回音 2 | 3 | 本例子中,W猫将带大家写一个大家都写过但是没什么人用过的TCP ECHO软件,作为本章的结尾。本程序仅作为实例程序,我个人估计也没有人在实际的生活中去使用她。不过,作为标准库的示例来说,已经足够。 4 | 5 | 首先,我们需要一个一个服务器端。 6 | 7 | ```rust 8 | fn server(addr: A) -> io::Result<()> { 9 | // 建立一个监听程序 10 | let listener = try!(TcpListener::bind(&addr)) ; 11 | // 这个程序一次只需处理一个链接就好 12 | for stream in listener.incoming() { 13 | // 通过match再次解包 stream到 14 | match stream { 15 | // 这里匹配的重点是如何将一个mut的匹配传给一个Result 16 | Ok(mut st) => { 17 | // 我们总是要求client端先发送数据 18 | // 准备一个超大的缓冲区 19 | // 当然了,在实际的生活中我们一般会采用环形缓冲来重复利用内存。 20 | // 这里仅作演示,是一种很低效的做法 21 | let mut buf: Vec = vec![0u8; 1024]; 22 | // 通过try!方法来解包 23 | // try!方法的重点是需要有特定的Error类型与之配合 24 | let rcount = try!(st.read(&mut buf)); 25 | // 只输出缓冲区里读取到的内容 26 | println!("{:?}", &buf[0..rcount]); 27 | // 回写内容 28 | let wcount = try!(st.write(&buf[0..rcount])); 29 | // 以下代码实际上算是逻辑处理 30 | // 并非标准库的一部分了 31 | if rcount != wcount { 32 | panic!("Not Fully Echo!, r={}, w={}", rcount, wcount); 33 | } 34 | // 清除掉已经读到的内容 35 | buf.clear(); 36 | } 37 | Err(e) => { 38 | panic!("{}", e); 39 | } 40 | } 41 | } 42 | // 关闭掉Serve端的链接 43 | drop(listener); 44 | Ok(()) 45 | } 46 | 47 | ``` 48 | 49 | 50 | 然后,我们准备一个模拟TCP短链接的客户端: 51 | 52 | ```rust 53 | fn client(addr: A) -> io::Result<()> { 54 | 55 | let mut buf = vec![0u8;1024]; 56 | loop { 57 | // 对比Listener,TcpStream就简单很多了 58 | // 本次模拟的是tcp短链接的过程,可以看作是一个典型的HTTP交互的基础IO模拟 59 | // 当然,这个通讯里面并没有HTTP协议 XD! 60 | let mut stream = TcpStream::connect(&addr).unwrap(); 61 | let msg = "WaySLOG comming!".as_bytes(); 62 | // 避免发送数据太快而刷屏 63 | thread::sleep_ms(100); 64 | let rcount = try!(stream.write(&msg)); 65 | let _ = try!(stream.read(&mut buf)); 66 | println!("{:?}", &buf[0..rcount]); 67 | buf.clear(); 68 | } 69 | Ok(()) 70 | } 71 | 72 | ``` 73 | 74 | 将我们的程序拼接起来如下: 75 | 76 | ```rust 77 | use std::net::*; 78 | use std::io; 79 | use std::io::{Read, Write}; 80 | use std::env; 81 | use std::thread; 82 | 83 | fn server(addr: A) -> io::Result<()> { .. } 84 | 85 | 86 | fn client(addr: A) -> io::Result<()> { .. } 87 | 88 | 89 | fn main() { 90 | let mut args = env::args(); 91 | args.next(); 92 | let action = args.next().unwrap(); 93 | if action == "s" { 94 | server(&args.next().unwrap()).unwrap(); 95 | } else { 96 | client(&args.next().unwrap()).unwrap(); 97 | } 98 | } 99 | 100 | ``` 101 | 102 | 各位可以自己试一下结果 103 | 104 | 105 | 写网络程序,注定了要处理各种神奇的条件和错误,定义自己的数据结构,粘包问题等都是需要我们去处理和关注的。相较而言,Rust本身在网络方面的基础设施建设并不尽如人意,甚至连网络I/O都只提供了如上的block I/O 。可能其团队更关注于语言基础语法特性和编译的改进,但其实,有着官方出品的这种网络库是非常重要的。同时,我也希望Rust能够涌现出更多的网络库方案,让Rust的明天更好更光明。 106 | -------------------------------------------------------------------------------- /intoborrow/cow.md: -------------------------------------------------------------------------------- 1 | # Cow 2 | 3 | 直译为奶牛!开玩笑。 4 | `Cow` 是一个枚举类型,通过 `use std::borrow::Cow;` 引入。它的定义是 `Clone-on-write`,即写时克隆。本质上是一个智能指针。 5 | 6 | 它有两个可选值: 7 | - `Borrowed`,用于包裹对象的引用(通用引用); 8 | - `Owned`,用于包裹对象的所有者; 9 | 10 | `Cow` 提供 11 | 12 | 1. 对此对象的不可变访问(比如可直接调用此对象原有的不可变方法); 13 | 2. 如果遇到需要修改此对象,或者需要获得此对象的所有权的情况,`Cow` 提供方法做克隆处理,并避免多次重复克隆。 14 | 15 | `Cow` 的设计目的是提高性能(减少复制)同时增加灵活性,因为大部分情况下,业务场景都是读多写少。利用 `Cow`,可以用统一,规范的形式实现,需要写的时候才做一次对象复制。这样就可能会大大减少复制的次数。 16 | 17 | 它有以下几个要点需要掌握: 18 | 19 | 1. `Cow` 能直接调用 `T` 的不可变方法,因为 `Cow` 这个枚举,实现了 `Deref`; 20 | 2. 在需要写 `T` 的时候,可以使用 `.to_mut()` 方法得到一个具有所有权的值的可变借用; 21 | 1. 注意,调用 `.to_mut()` 不一定会产生克隆; 22 | 2. 在已经具有所有权的情况下,调用 `.to_mut()` 有效,但是不会产生新的克隆; 23 | 3. 多次调用 `.to_mut()` 只会产生一次克隆。 24 | 3. 在需要写 `T` 的时候,可以使用 `.into_owned()` 创建新的拥有所有权的对象,这个过程往往意味着内存拷贝并创建新对象; 25 | 1. 如果之前 `Cow` 中的值是借用状态,调用此操作将执行克隆; 26 | 2. 本方法,参数是`self`类型,它会“吃掉”原先的那个对象,调用之后原先的对象的生命周期就截止了,在 `Cow` 上不能调用多次; 27 | 28 | 29 | ## 举例 30 | 31 | `.to_mut()` 举例 32 | 33 | ```rust 34 | use std::borrow::Cow; 35 | 36 | let mut cow: Cow<[_]> = Cow::Owned(vec![1, 2, 3]); 37 | 38 | let hello = cow.to_mut(); 39 | 40 | assert_eq!(hello, &[1, 2, 3]); 41 | ``` 42 | 43 | `.into_owned()` 举例 44 | 45 | ```rust 46 | use std::borrow::Cow; 47 | 48 | let cow: Cow<[_]> = Cow::Owned(vec![1, 2, 3]); 49 | 50 | let hello = cow.into_owned(); 51 | 52 | assert_eq!(vec![1, 2, 3], hello); 53 | ``` 54 | 55 | 综合举例 56 | 57 | ```rust 58 | use std::borrow::Cow; 59 | 60 | fn abs_all(input: &mut Cow<[i32]>) { 61 | for i in 0..input.len() { 62 | let v = input[i]; 63 | if v < 0 { 64 | // clones into a vector the first time (if not already owned) 65 | input.to_mut()[i] = -v; 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | ## `Cow` 在函数返回值上的应用实例 72 | 73 | 题目:写一个函数,过滤掉输入的字符串中的所有空格字符,并返回过滤后的字符串。 74 | 75 | 对这个简单的问题,不用思考,我们都可以很快写出代码: 76 | 77 | ```rust 78 | fn remove_spaces(input: &str) -> String { 79 | let mut buf = String::with_capacity(input.len()); 80 | 81 | for c in input.chars() { 82 | if c != ' ' { 83 | buf.push(c); 84 | } 85 | } 86 | 87 | buf 88 | } 89 | ``` 90 | 91 | 设计函数输入参数的时候,我们会停顿一下,这里,用 `&str` 好呢,还是 `String` 好呢?思考一番,从性能上考虑,有如下结论: 92 | 93 | 1. 如果使用 `String`, 则外部在调用此函数的时候, 94 | 1. 如果外部的字符串是 `&str`,那么,它需要做一次克隆,才能调用此函数; 95 | 2. 如果外部的字符串是 `String`,那么,它不需要做克隆,就可以调用此函数。但是,一旦调用后,外部那个字符串的所有权就被 `move` 到此函数中了,外部的后续代码将无法再使用原字符串。 96 | 2. 如果使用 `&str`,则不存在上述两个问题。但可能会遇到生命周期的问题,需要注意。 97 | 98 | 继续分析上面的例子,我们发现,在函数体内,做了一次新字符串对象的生成和拷贝。 99 | 100 | 让我们来仔细分析一下业务需求。最坏的情况下,如果字符串中没有空白字符,那最好是直接原样返回。这种情况做这样一次对象的拷贝,完全就是浪费了。 101 | 102 | 于是我们心想改进这个算法。很快,又遇到了另一个问题,返回值是 `String` 的嘛,我不论怎样,要把 `&str` 转换成 `String` 返回,始终都要经历一次复制。于是我们快要放弃了。 103 | 104 | 好吧,`Cow`君这时出马了。奶牛君很快写出了如下代码: 105 | 106 | ```rust 107 | use std::borrow::Cow; 108 | 109 | fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> { 110 | if input.contains(' ') { 111 | let mut buf = String::with_capacity(input.len()); 112 | 113 | for c in input.chars() { 114 | if c != ' ' { 115 | buf.push(c); 116 | } 117 | } 118 | 119 | return Cow::Owned(buf); 120 | } 121 | 122 | return Cow::Borrowed(input); 123 | } 124 | 125 | ``` 126 | 127 | 完美解决了业务逻辑与返回值类型冲突的问题。本例可细细品味。 128 | 129 | 外部程序,拿到这个 `Cow` 返回值后,按照我们上文描述的 `Cow` 的特性使用就好了。 130 | -------------------------------------------------------------------------------- /quickstart/function-method.md: -------------------------------------------------------------------------------- 1 | # 函数与方法 2 | 3 | ## 函数 4 | 5 | 要声明一个函数,需要使用关键字`fn`,后面跟上函数名,比如 6 | 7 | ```rust 8 | fn add_one(x: i32) -> i32 { 9 | x + 1 10 | } 11 | ``` 12 | 13 | 其中函数参数的类型不能省略,可以有多个参数,但是最多只能返回一个值, 14 | 提前返回使用`return`关键字。Rust编译器会对未使用的函数提出警告, 15 | 可以使用属性`#[allow(dead_code)]`禁用无效代码检查。 16 | 17 | Rust有一个特殊特性适用于发散函数 (diverging function),它不返回: 18 | 19 | ```rust 20 | fn diverges() -> ! { 21 | panic!("This function never returns!"); 22 | } 23 | ``` 24 | 25 | 其中`panic!`是一个宏,使当前执行线程崩溃并打印给定信息。返回类型`!`可用作任何类型: 26 | 27 | ```rust 28 | let x: i32 = diverges(); 29 | let y: String = diverges(); 30 | ``` 31 | 32 | ## 匿名函数 33 | 34 | Rust使用闭包 (closure) 来创建匿名函数: 35 | 36 | ```rust 37 | let num = 5; 38 | let plus_num = |x: i32| x + num; 39 | ``` 40 | 41 | 其中闭包`plus_num`借用了它作用域中的`let`绑定`num`。如果要让闭包获得所有权, 42 | 可以使用`move`关键字: 43 | 44 | ```rust 45 | let mut num = 5; 46 | 47 | { 48 | let mut add_num = move |x: i32| num += x; // 闭包通过move获取了num的所有权 49 | 50 | add_num(5); 51 | } 52 | 53 | // 下面的num在被move之后还能继续使用是因为其实现了Copy特性 54 | // 具体可见所有权(Owership)章节 55 | assert_eq!(5, num); 56 | ``` 57 | 58 | ## 高阶函数 59 | 60 | Rust 还支持高阶函数 (high order function),允许把闭包作为参数来生成新的函数: 61 | 62 | ```rust 63 | fn add_one(x: i32) -> i32 { x + 1 } 64 | 65 | fn apply(f: F, y: i32) -> i32 66 | where F: Fn(i32) -> i32 67 | { 68 | f(y) * y 69 | } 70 | 71 | fn factory(x: i32) -> Box i32> { 72 | Box::new(move |y| x + y) 73 | } 74 | 75 | fn main() { 76 | let transform: fn(i32) -> i32 = add_one; 77 | let f0 = add_one(2i32) * 2; 78 | let f1 = apply(add_one, 2); 79 | let f2 = apply(transform, 2); 80 | println!("{}, {}, {}", f0, f1, f2); 81 | 82 | let closure = |x: i32| x + 1; 83 | let c0 = closure(2i32) * 2; 84 | let c1 = apply(closure, 2); 85 | let c2 = apply(|x| x + 1, 2); 86 | println!("{}, {}, {}", c0, c1, c2); 87 | 88 | let box_fn = factory(1i32); 89 | let b0 = box_fn(2i32) * 2; 90 | let b1 = (*box_fn)(2i32) * 2; 91 | let b2 = (&box_fn)(2i32) * 2; 92 | println!("{}, {}, {}", b0, b1, b2); 93 | 94 | let add_num = &(*box_fn); 95 | let translate: &Fn(i32) -> i32 = add_num; 96 | let z0 = add_num(2i32) * 2; 97 | let z1 = apply(add_num, 2); 98 | let z2 = apply(translate, 2); 99 | println!("{}, {}, {}", z0, z1, z2); 100 | } 101 | ``` 102 | 103 | ## 方法 104 | 105 | Rust通过`impl`关键字在`struct`、`enum`或者`trait`对象上实现方法调用语法 (method call syntax)。 106 | 关联函数 (associated function) 的第一个参数通常为`self`参数,有3种变体: 107 | * `self`,允许实现者移动和修改对象,对应的闭包特性为`FnOnce`。 108 | * `&self`,既不允许实现者移动对象也不允许修改,对应的闭包特性为`Fn`。 109 | * `&mut self`,允许实现者修改对象但不允许移动,对应的闭包特性为`FnMut`。 110 | 111 | 不含`self`参数的关联函数称为静态方法 (static method)。 112 | 113 | ```rust 114 | struct Circle { 115 | x: f64, 116 | y: f64, 117 | radius: f64, 118 | } 119 | 120 | impl Circle { 121 | fn new(x: f64, y: f64, radius: f64) -> Circle { 122 | Circle { 123 | x: x, 124 | y: y, 125 | radius: radius, 126 | } 127 | } 128 | 129 | fn area(&self) -> f64 { 130 | std::f64::consts::PI * (self.radius * self.radius) 131 | } 132 | } 133 | 134 | fn main() { 135 | let c = Circle { x: 0.0, y: 0.0, radius: 2.0 }; 136 | println!("{}", c.area()); 137 | 138 | // use associated function and method chaining 139 | println!("{}", Circle::new(0.0, 0.0, 2.0).area()); 140 | } 141 | ``` 142 | -------------------------------------------------------------------------------- /std/fs-and-path.md: -------------------------------------------------------------------------------- 1 | # 目录操作:简单grep 2 | 3 | 上一节我们实现了通过`Command`调用subprocess。这一节,我们将通过自己的代码去实现一个简单的grep。当然了,这种基础的工具你是能找到源码的,而我们的实现也并不像真正的grep那样注重效率,本节的主要作用就在于演示标准库API的使用。 4 | 5 | 首先,我们需要对当前目录进行递归,遍历,每当查找到文件的时候,我们回调一个函数。 6 | 7 | 于是,我们就有了这么个函数: 8 | 9 | ```rust 10 | use std::env::args; 11 | use std::io; 12 | use std::fs::{self, File, DirEntry}; 13 | use std::path::Path; 14 | 15 | fn visit_dirs(dir: &Path, pattern: &String, cb: &Fn(&DirEntry, &String)) -> io::Result<()> { 16 | if try!(fs::metadata(dir)).is_dir() { 17 | for entry in try!(fs::read_dir(dir)) { 18 | let entry = try!(entry); 19 | if try!(fs::metadata(entry.path())).is_dir() { 20 | try!(visit_dirs(&entry.path(), pattern, cb)); 21 | } else { 22 | cb(&entry, pattern); 23 | } 24 | } 25 | }else{ 26 | let entry = try!(try!(fs::read_dir(dir)).next().unwrap()); 27 | cb(&entry, pattern); 28 | } 29 | Ok(()) 30 | } 31 | 32 | ``` 33 | 34 | 我们有了这样的一个函数,有同学可能觉得这代码眼熟。这不是标准库里的例子改了一下么? 35 | 36 | . 37 | 38 | . 39 | 40 | . 41 | 42 | 是啊! 43 | 44 | 好了,继续,我们需要读取每个查到的文件,同时判断每一行里有没有所查找的内容。 45 | 我们用一个BufferIO去读取各个文件,同时用String的自带方法来判断内容是否存在。 46 | 47 | ```rust 48 | fn call_back(de: &DirEntry, pt: &String) { 49 | let mut f = File::open(de.path()).unwrap(); 50 | let mut buf = io::BufReader::new(f); 51 | for line in io::BufRead::lines(buf) { 52 | let line = line.unwrap_or("".to_string()); 53 | if line.contains(pt) { 54 | println!("{}", &line); 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | 最后,我们将整个函数调用起来,如下: 61 | 62 | ```rust 63 | use std::env::args; 64 | use std::io; 65 | use std::fs::{self, File, DirEntry}; 66 | use std::path::Path; 67 | 68 | fn visit_dirs(dir: &Path, pattern: &String, cb: &Fn(&DirEntry, &String)) -> io::Result<()> { 69 | if try!(fs::metadata(dir)).is_dir() { 70 | for entry in try!(fs::read_dir(dir)) { 71 | let entry = try!(entry); 72 | if try!(fs::metadata(entry.path())).is_dir() { 73 | try!(visit_dirs(&entry.path(), pattern, cb)); 74 | } else { 75 | cb(&entry, pattern); 76 | } 77 | } 78 | }else{ 79 | let entry = try!(try!(fs::read_dir(dir)).next().unwrap()); 80 | cb(&entry, pattern); 81 | } 82 | Ok(()) 83 | } 84 | 85 | fn call_back(de: &DirEntry, pt: &String) { 86 | let mut f = File::open(de.path()).unwrap(); 87 | let mut buf = io::BufReader::new(f); 88 | for line in io::BufRead::lines(buf) { 89 | let line = line.unwrap_or("".to_string()); 90 | if line.contains(pt) { 91 | println!("{}", &line); 92 | } 93 | } 94 | } 95 | 96 | // 实现调用grep命令搜索文件 97 | fn main() { 98 | let mut arg_iter = args(); 99 | arg_iter.next(); 100 | // panic if there is no one 101 | let pattern = arg_iter.next().unwrap_or("main".to_string()); 102 | let pt = arg_iter.next().unwrap_or("./".to_string()); 103 | let pt = Path::new(&pt); 104 | visit_dirs(&pt, &pattern, &call_back).unwrap(); 105 | } 106 | 107 | ``` 108 | 109 | 调用如下: 110 | 111 | ``` 112 | ➜ demo git:(master) ✗ ./target/debug/demo "fn main()" ../ 113 | fn main() { 114 | fn main() { } 115 | fn main() { 116 | pub fn main() { 117 | pub fn main() {} 118 | fn main() { 119 | pub fn main() { 120 | pub fn main() {} 121 | ``` 122 | -------------------------------------------------------------------------------- /intoborrow/into.md: -------------------------------------------------------------------------------- 1 | # Into/From 及其在 String 和 &str 互转上的应用 2 | 3 | `std::convert` 下面,有两个 Trait,`Into/From`,它们是一对孪生姐妹。它们的作用是配合泛型,进行一些设计上的归一化处理。 4 | 5 | 它们的基本形式为: `From` 和 `Into`。 6 | 7 | ## From 8 | 9 | 对于类型为 `U` 的对象 `foo`,如果它实现了 `From`,那么,可以通过 `let foo = U::from(bar)` 来生成自己。这里,`bar` 是类型为 `T` 的对象。 10 | 11 | 下面举一例,因为 `String` 实现了 `From<&str>`,所以 `String` 可以从 `&str` 生成。 12 | 13 | ```rust 14 | let string = "hello".to_string(); 15 | let other_string = String::from("hello"); 16 | 17 | assert_eq!(string, other_string); 18 | ``` 19 | 20 | ## Into 21 | 22 | 对于一个类型为 `U: Into` 的对象 `foo`,`Into` 提供了一个函数:`.into(self) -> T`,调用 `foo.into()` 会消耗自己(转移资源所有权),生成类型为 `T` 的另一个新对象 `bar`。 23 | 24 | 这句话,说起来有点抽象。下面拿一个具体的实例来辅助理解。 25 | 26 | ```rust 27 | fn is_hello>>(s: T) { 28 | let bytes = b"hello".to_vec(); 29 | assert_eq!(bytes, s.into()); 30 | } 31 | 32 | let s = "hello".to_string(); 33 | is_hello(s); 34 | ``` 35 | 36 | 因为 `String` 类型实现了 `Into>`。 37 | 38 | 下面拿一个实际生产中字符串作为函数参数的例子来说明。 39 | 40 | 在我们设计库的 `API` 的时候,经常会遇到一个恼人的问题,函数参数如果定为 `String`,则外部传入实参的时候,对字符串字面量,必须要做 `.to_string()` 或 `.to_owned()` 转换,参数一多,就是一件又乏味又丑的事情。(而反过来设计的话,对初学者来说,又会遇到一些生命周期的问题,比较麻烦,这个后面论述) 41 | 42 | 那存不存在一种方法,能够使传参又能够接受 `String` 类型,又能够接受 `&str` 类型呢?答案就是**泛型**。而仅是泛型的话,太宽泛。因此,标准库中,提供了 `Into` 来为其做约束,以便方便而高效地达到我们的目的。 43 | 44 | 比如,我们有如下结构体: 45 | 46 | ```rust 47 | struct Person { 48 | name: String, 49 | } 50 | 51 | impl Person { 52 | fn new (name: String) -> Person { 53 | Person { name: name } 54 | } 55 | } 56 | ``` 57 | 58 | 我们在调用的时候,是这样的: 59 | 60 | ```rust 61 | let name = "Herman".to_string(); 62 | let person = Person::new(name); 63 | ``` 64 | 65 | 如果直接写成: 66 | 67 | ```rust 68 | let person = Person::new("Herman"); 69 | ``` 70 | 71 | 就会报类型不匹配的错误。 72 | 73 | 好了,下面 `Into` 出场。我们可以定义结构体为 74 | 75 | ```rust 76 | struct Person { 77 | name: String, 78 | } 79 | 80 | impl Person { 81 | fn new>(name: S) -> Person { 82 | Person { name: name.into() } 83 | } 84 | } 85 | ``` 86 | 87 | 然后,调用的时候,下面两种写法都是可以的: 88 | 89 | ```rust 90 | fn main() { 91 | let person = Person::new("Herman"); 92 | let person = Person::new("Herman".to_string()); 93 | } 94 | ``` 95 | 96 | 我们来仔细分析一下这一块的写法 97 | 98 | ```rust 99 | impl Person { 100 | fn new>(name: S) -> Person { 101 | Person { name: name.into() } 102 | } 103 | } 104 | ``` 105 | 106 | 参数类型为 `S`, 是一个泛型参数,表示可以接受不同的类型。`S: Into` 表示 `S` 类型必须实现了 `Into`(约束)。而 `&str` 类型,符合这个要求。因此 `&str` 类型可以直接传进来。 107 | 108 | 而 `String` 本身也是实现了 `Into` 的。当然也可以直接传进来。 109 | 110 | 然后,下面 `name: name.into()` 这里也挺神秘的。它的作用是将 `name` 转换成 `String` 类型的另一个对象。当 name 是 `&str` 时,它会转换成 `String` 对象,会做一次字符串的拷贝(内存的申请、复制)。而当 name 本身是 `String` 类型时,`name.into()` 不会做任何转换,代价为零(有没有恍然大悟)。 111 | 112 | 根据参考资料,上述内容通过下面三式获得: 113 | 114 | ```rust 115 | impl<'a> From<&'a str> for String {} 116 | impl From for T {} 117 | impl Into for T where U: From {} 118 | ``` 119 | 120 | 更多内容,请参考: 121 | 122 | - [http://doc.rust-lang.org/std/convert/trait.Into.html](http://doc.rust-lang.org/std/convert/trait.Into.html) 123 | - [http://doc.rust-lang.org/std/convert/trait.From.html](http://doc.rust-lang.org/std/convert/trait.From.html) 124 | - [http://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html](http://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html) 125 | -------------------------------------------------------------------------------- /function/return_value.md: -------------------------------------------------------------------------------- 1 | # 函数返回值 2 | 在rust中,任何函数都有返回类型,当函数返回时,会返回一个该类型的值。我们先来看看main函数: 3 | 4 | ```rust 5 | fn main() { 6 | //statements 7 | } 8 | ``` 9 | 10 | 之前有说过,函数的返回值类型是在参数列表后,加上箭头和类型来指定的。不过,一般我们看到的main函数的定义并没有这么做。这是因为main函数的返回值是`()`,在rust中,当一个函数返回`()`时,可以省略。main函数的完整形式如下: 11 | 12 | ```rust 13 | fn main() -> () { 14 | //statements 15 | } 16 | ``` 17 | 18 | main函数的返回值类型是`()`,它是一个特殊的元组——没有元素的元组,称为`unit`,它表示一个函数没有任何信息需要返回。在Rust Reference的[Types](https://doc.rust-lang.org/reference/types.html#tuple-types)中是的描述如下: 19 | > For historical reasons and convenience, the tuple type with no elements (`()`) is often called ‘unit’ or ‘the unit type’. 20 | 21 | `()`类型,其实类似于C/C++、Java、C#中的`void`类型。 22 | 23 | 下面来看一个有返回值的例子: 24 | 25 | ```rust 26 | fn main() { 27 | let a = 3; 28 | println!("{}", inc(a)); 29 | } 30 | 31 | fn inc(n: i32) -> i32 { 32 | n + 1 33 | } 34 | ``` 35 | 36 | 上面的例子中,函数`inc`有一个`i32`类型的参数和返回值,作用是将参数加1返回。需要注意的是`inc`函数中只有`n+1`一个表达式,并没有像C/C++或Java、C#等语言有显式地`return`语句类返回一个值。这是因为,与其他基于语句的语言(如C语言)不同,rust是基于表达式的语言,函数中最后一个表达式的值,默认作为返回值。当然,rust中也有语句,关于rust的语句和表达式,请看[下一节](statement_expression.md)。 37 | 38 | ## return关键字 39 | rust也有`return`关键字,不过一般用于提前返回。来看一个简单地例子: 40 | 41 | ```rust 42 | fn main() { 43 | let a = [1,3,2,5,9,8]; 44 | println!("There is 7 in the array: {}", find(7, &a)); 45 | println!("There is 8 in the array: {}", find(8, &a)); 46 | } 47 | 48 | fn find(n: i32, a: &[i32]) -> bool { 49 | for i in a { 50 | if *i == n { 51 | return true; 52 | } 53 | } 54 | false 55 | } 56 | ``` 57 | 58 | 上例中,`find`函数,接受一个`i32`类型`n`和一个`i32`类型的切片(`slice`)`a`,返回一个`bool`值,若n是a的元素,则返回`true`,否则返回`false`。可以看到,`return`关键字,用在`for`循环的`if`表达式中,若此时a的元素与n相等,则立刻返回true,剩下的循环不必再进行,否则一直循环检测完整个切片(slice),最后返回false。当然,return语句也可以用在最后返回,像C/C++一样使用:把`find`函数最后一句`false`改为`return false;`(注意分号不可省略)也是可以的,不过这就不是rust的编程风格了。这里需要注意的是,`for`循环中的`i`,其类型为`&i32`,需要使用解引用操作符来变换为`i32`类型。另外,切片(slice)在这里可以看作是对数组的引用,关于切片与数组的详细解释可以看[Rust Reference](https://doc.rust-lang.org/reference.html#array-and-slice-types)和[rustbyexample](http://rustbyexample.com/primitives/array.html)中的相关内容。 59 | 60 | ## 返回多个值 61 | rust的函数不支持多返回值,但是我们可以利用元组来返回多个值,配合rust的模式匹配,使用起来十分灵活。先看例子: 62 | 63 | ```rust 64 | fn main() { 65 | let (p2,p3) = pow_2_3(789); 66 | println!("pow 2 of 789 is {}.", p2); 67 | println!("pow 3 of 789 is {}.", p3); 68 | } 69 | 70 | fn pow_2_3(n: i32) -> (i32, i32) { 71 | (n*n, n*n*n) 72 | } 73 | ``` 74 | 75 | 可以看到,上例中,`pow_2_3`函数接收一个`i32`类型的值,返回其二次方和三次方的值,这两个值包装在一个元组中返回。在`main`函数中,`let`语句就可以使用模式匹配将函数返回的元组进行解构,将这两个返回值分别赋给`p2`和`p3`,从而可以得到`789`二次方的值和三次方的值。 76 | 77 | ## 发散函数 78 | 发散函数(diverging function)是rust中的一个特性。发散函数不返回,它使用感叹号`!`作为返回类型表示: 79 | 80 | ```rust 81 | fn main() { 82 | println!("hello"); 83 | diverging(); 84 | println!("world"); 85 | } 86 | 87 | fn diverging() -> ! { 88 | panic!("This function will never return"); 89 | } 90 | ``` 91 | 92 | 由于发散函数不会返回,所以就算其后再有其他语句也是不会执行的。倘若其后还有其他语句,会出现如下编译警告:![error](../images/function-return-value.png)。当然了,我们要知道的是不发散的函数也是可以不返回的,比如无限循环之类的。 93 | 发散函数一般都以`panic!`宏调用或其他调用其他发散函数结束,所以,调用发散函数会导致当前线程崩溃。[Rust Reference 6.1.3.2 Diverging functions][ref]中的描述如下: 94 | > We call such functions "diverging" because they never return a value to the caller. Every control path in a diverging function must end with a panic!() or a call to another diverging function on every control path. The ! annotation does not denote a type. 95 | 96 | [ref]:http://doc.rust-lang.org/reference.html#diverging-functions 97 | -------------------------------------------------------------------------------- /io/io.md: -------------------------------------------------------------------------------- 1 | # 标准输入与输出 2 | 3 | 回顾一下我们写的第一个 Rust 程序就是带副作用的,其副作用就是向标准输出(stdout),通常是终端或屏幕,输出了 Hello, World! 让屏幕上这几个字符的地方点亮起来。`println!` 宏是最常见的输出,用宏来做输出的还有 `print!`,两者都是向标准输出(stdout)输出,两者的区别也一眼就能看出。至于格式化输出,[基础运算符和字符串格式化小节](../type/operator-and-formatting.md)有详细说明,这里就不再啰嗦了。 4 | 5 | 更通用的标准输入与输出定义在 `std::io` 模块里,调用 `std::io::stdin()` 和 `std::io::stdout()` 两个函数分别会得到输入句柄和输出句柄(哎,[句柄](https://zh.wikipedia.org/wiki/%E5%8F%A5%E6%9F%84)这个词是计算机史上最莫名其妙的翻译了),这两个句柄默认会通过互斥锁同步,也就是说不让多个进程同时读或写标准输入输出,不然的话如果一个进程要往标准输出画马,一个进程要画驴,两个进程同时写标准输出的话,最后可能就给画出一头骡子了,如果更多进程画不同的动物最后可能就成四不像了。除了隐式地用互斥锁,我们还可以显式地在句柄上调用 `.lock()`。输入输出句柄实现了前面讲的读写 Trait,所以是 reader/writer,就可以调接口来读写标准输入与输出了。举几个栗子: 6 | 7 | ```rust 8 | use std::io; 9 | 10 | fn read_from_stdin(buf: &mut String) -> io::Result<()> { 11 | try!(io::stdin().read_line(buf)); 12 | Ok(()) 13 | } 14 | ``` 15 | 16 | ```rust 17 | use std::io; 18 | 19 | fn write_to_stdout(buf: &[u8]) -> io::Result<()> { 20 | try!(io::stdout().write(&buf)); 21 | Ok(()) 22 | } 23 | ``` 24 | 25 | 可以看到上面的例子都是返回了 `io::Result<()>` 类型,这不是偶然,而是 IO 操作通用的写法,因为 IO 操作是程序与外界打交道,所以都是有可能失败的,用 `io::Result` 把结果包起来,`io::Result` 只是标准 `Result` 中 `E` 固定为 `io::Error` 后类型的别名,而作为有副作用的操作我们一般是不用关心其返回值的,因为执行这类函数其真正的意义都体现在副作用上面了,所以返回值只是用来表示是否成功执行,而本身 `Result` 类型本身已经可以表示执行状态了,里面的 `T` 是什么则无关紧要,既然 `T` 没什么意义,那我们就选没什么意义的 `unit` 类型好了,所以 IO 操作基本上都是使用 `io::Result<()>`。 26 | 27 | 另外有一个地方需要注意的是由于 IO 操作可能会失败所以一般都是和 `try!` 宏一起使用的,但是 `try!` 在遇到错误时会把错误 `return` 出去的,所以需要保证包含 `try!` 语句的函数其返回类型是 `io::Result`,很多新手文档没仔细看就直接查 std api 文档,然后照着 api 文档里面的例子把带 IO 操作的 `try!` 宏写到了 `main` 函数里。结果一编译,擦,照着文档写都编译不过,什么烂文档。其实点一下 api 文档上面的运行按钮就会发现文档里面的例子都是把 `try!` 放在另一个函数里面的,因为 `main` 函数是没有返回值的,而 `try!` 会返回 `io::Result`,所以直接把 `try!` 放 `main` 函数里面肯定要跪。比如下面的从标准输入读取一行输入,由于把 `try!` 放在了 main 函数里,所以是编译不过的。 28 | 29 | ```rust 30 | use std::io; 31 | 32 | fn main() { 33 | let mut input = String::new(); 34 | try!(io::stdin().read_line(&mut input)); 35 | println!("You typed: {}", input.trim()); 36 | } 37 | ``` 38 | 39 | 这里有一件事需要主要的是 Rust 里面没有办法从键盘获取一个数字类型的值。实际上像 C 这样的语言也不是直接获取了数字类型,它只不过是做了一种转换。那么我们如果想要从键盘获取一个数字类型应该怎么做呢? 40 | 41 | ```rust 42 | fn main() { 43 | let mut input = String::new(); 44 | std::io::stdin() 45 | .read_line(&mut input) 46 | .expect("Failed to read line"); 47 | // 这里等效的写法是: 48 | // let num: i32 = input.trim().parse().unwrap(); 49 | let num = input.trim().parse::().unwrap(); 50 | println!("您输入的数字是:{}", num); 51 | } 52 | ``` 53 | 54 | 如果有很多地方都需要输入数字可以自行编写一个 `numin` 宏: 55 | 56 | ```rust 57 | macro_rules! numin { 58 | () =>{ 59 | { 60 | let mut input = String::new(); 61 | std::io::stdin() 62 | .read_line(&mut input) 63 | .expect("Failed to read line"); 64 | input.trim().parse().unwrap() 65 | } 66 | }; 67 | } 68 | ``` 69 | 70 | 于是上面的程序可以被改写成: 71 | 72 | ``` 73 | 74 | fn main() { 75 | let num: i32 = numin!(); 76 | println!("您输入的数字是:{}", num); 77 | } 78 | ``` 79 | 80 | 不过如果用户输入的不是数字,那么就会导致错误。这一点和 C 里面是非常相似的。当然您可以把程序写得再复杂一点儿来保证用户输入的一定是数字。不过这些就不是我们这一节要讨论的内容了。 81 | 82 | 还有一点一些从其它语言转过来的程序员可能会疑惑的是,如何从命令行接受输入参数,因为 C 里面的 main 函数可以带参数所以可以直接从 main 函数的参数里获取输入参数。但其实这类输入与我们这里讲的有很大的差别的,它在 Rust 里面被归为环境变量,可以通过 `std::env::args()` 获取,这个函数返回一个 `Args` 迭代器,其中第一个就是程序名,后面的都是输入给程序的命令行参数。 83 | 84 | ```rust 85 | use std::env; 86 | 87 | fn main() { 88 | let args = env::args(); 89 | for arg in args { 90 | println!("{}", arg); 91 | } 92 | } 93 | ``` 94 | 95 | 将上面的程序存为 *args.rs* 然后编译执行,结果如下 96 | 97 | ``` 98 | $ rustc args.rs 99 | $ ./args a b c 100 | ./args 101 | a 102 | b 103 | c 104 | ``` 105 | 106 | -------------------------------------------------------------------------------- /std/process.md: -------------------------------------------------------------------------------- 1 | # 系统命令:调用grep 2 | 3 | 我们知道,Linux系统中有一个命令叫grep,他能对目标文件进行分析并查找相应字符串,并该字符串所在行输出。 4 | 今天,我们先来写一个Rust程序,来调用一下这个 grep 命令 5 | 6 | ```rust 7 | use std::process::*; 8 | use std::env::args; 9 | 10 | // 实现调用grep命令搜索文件 11 | fn main() { 12 | let mut arg_iter = args(); 13 | // panic if there is no one 14 | arg_iter.next().unwrap(); 15 | let pattern = arg_iter.next().unwrap_or("main".to_string()); 16 | let pt = arg_iter.next().unwrap_or("./".to_string()); 17 | let output = Command::new("/usr/bin/grep") 18 | .arg("-n") 19 | .arg("-r") 20 | .arg(&pattern) 21 | .arg(&pt) 22 | .output() 23 | .unwrap_or_else(|e| panic!("wg panic because:{}", e)); 24 | println!("output:"); 25 | let st = String::from_utf8_lossy(&output.stdout); 26 | let lines = st.split("\n"); 27 | for line in lines { 28 | println!("{}", line); 29 | } 30 | } 31 | 32 | ``` 33 | 34 | 看起来好像还不错,但是,以上的程序有一个比较致命的缺点——因为Output是同步的,因此,一旦调用的目录下有巨大的文件,grep的分析将占用巨量的时间。这对于一个高可用的程序来说是不被允许的。 35 | 36 | 那么如何改进呢? 37 | 38 | 其实在上面的代码中,我们隐藏了一个 `Child` 的概念,即——子进程。 39 | 40 | 下面我来演示怎么操作子进程: 41 | 42 | ```rust 43 | use std::process::*; 44 | use std::env::args; 45 | 46 | // 实现调用grep命令搜索文件 47 | fn main() { 48 | let mut arg_iter = args(); 49 | // panic if there is no one 50 | arg_iter.next(); 51 | let pattern = arg_iter.next().unwrap_or("main".to_string()); 52 | let pt = arg_iter.next().unwrap_or("./".to_string()); 53 | let child = Command::new("grep") 54 | .arg("-n") 55 | .arg("-r") 56 | .arg(&pattern) 57 | .arg(&pt) 58 | .spawn().unwrap(); 59 | // 做些其他的事情 60 | std::thread::sleep_ms(1000); 61 | println!("{}", "计算很费时间……"); 62 | let out = child.wait_with_output().unwrap(); 63 | let out_str = String::from_utf8_lossy(&out.stdout); 64 | for line in out_str.split("\n") { 65 | println!("{}", line); 66 | } 67 | } 68 | 69 | ``` 70 | 71 | 但是,这个例子和我们预期的并不太一样! 72 | 73 | ``` 74 | ./demo main /home/wayslog/rust/demo/src 75 | /home/wayslog/rust/demo/src/main.rs:5:fn main() { 76 | /home/wayslog/rust/demo/src/main.rs:9: let pattern = arg_iter.next().unwrap_or("main".to_string()); 77 | 计算很费时间…… 78 | 79 | ``` 80 | 81 | 为什么呢? 82 | 83 | 很简单,我们知道,在Linux中,`fork`出来的函数会继承父进程的所有句柄。因此,子进程也就会继承父进程的标准输出,也就是造成了这样的问题。这也是最后我们用out无法接收到最后的输出也就知道了,因为在前面已经被输出出来了呀! 84 | 85 | 那么怎么做呢?给这个子进程一个pipeline就好了! 86 | 87 | ```rust 88 | use std::process::*; 89 | use std::env::args; 90 | 91 | // 实现调用grep命令搜索文件 92 | fn main() { 93 | let mut arg_iter = args(); 94 | // panic if there is no one 95 | arg_iter.next(); 96 | let pattern = arg_iter.next().unwrap_or("main".to_string()); 97 | let pt = arg_iter.next().unwrap_or("./".to_string()); 98 | let child = Command::new("grep") 99 | .arg("-n") 100 | .arg("-r") 101 | .arg(&pattern) 102 | .arg(&pt) 103 | // 设置pipeline 104 | .stdout(Stdio::piped()) 105 | .spawn().unwrap(); 106 | // 做些其他的事情 107 | std::thread::sleep_ms(1000); 108 | println!("{}", "计算很费时间……"); 109 | let out = child.wait_with_output().unwrap(); 110 | let out_str = String::from_utf8_lossy(&out.stdout); 111 | for line in out_str.split("\n") { 112 | println!("{}", line); 113 | } 114 | } 115 | ``` 116 | 117 | 这段代码相当于给了`stdout`一个缓冲区,这个缓冲区直到我们计算完成之后才被读取,因此就不会造成乱序输出的问题了。 118 | 119 | 这边需要注意的一点是,一旦你开启了一个子进程,那么,无论你程序是怎么处理的,最后一定要记得对这个`child`调用`wait`或者`wait_with_output`,除非你显式地调用`kill`。因为如果父进程不`wait`它的话,它将会变成一个僵尸进程!!! 120 | 121 | *注*: 以上问题为Linux下Python多进程的日常问题,已经见怪不怪了。 122 | -------------------------------------------------------------------------------- /function/statement_expression.md: -------------------------------------------------------------------------------- 1 | # 语句和表达式 2 | rust是一个基于表达式的语言,不过它也有语句。rust只有两种语句:声明语句和表达式语句,其他的都是表达式。基于表达式是函数式语言的一个重要特征,表达式总是返回值。 3 | 4 | ## 声明语句 5 | rust的声明语句可以分为两种,一种为变量声明语句,另一种为Item声明语句。 6 | 1. 变量声明语句。主要是指`let`语句,如: 7 | 8 | ```rust 9 | let a = 8; 10 | let b: Vec = Vec::new(); 11 | let (a, c) = ("hi", false); 12 | ``` 13 | 14 | 由于let是语句,所以不能将let语句赋给其他值。如下形式是错误的: 15 | 16 | ```rust 17 | let b = (let a = 8); 18 | ``` 19 | 20 | rustc编译器会给出错误信息:![error](../images/function-statement-expression.png) 21 | 22 | 2. Item声明。是指函数(function)、结构体(structure)、类型别名(type)、静态变量(static)、特质(trait)、实现(implementation)或模块(module)的声明。这些声明可以嵌套在任意块(block)中。关于Item声明,Rust Reference中的描述如下: 23 | > An item declaration statement has a syntactic form identical to an item declaration within a module. Declaring an item — a function, enumeration, structure, type, static, trait, implementation or module — locally within a statement block is simply a way of restricting its scope to a narrow region containing all of its uses; it is otherwise identical in meaning to declaring the item outside the statement block. 24 | 25 | 当然,这里不能展开讲这些Item都是如何声明的,详情请看RustPrimer的其他相关章节。 26 | 27 | ## 表达式语句 28 | 表达式语句,由一个表达式和一个分号组成,即在表达式后面加一个分号就将一个表达式转变为了一个语句。所以,有多少种表达式,就有多少种表达式语句。 29 | 30 | __rust有许多种表达式:__ 31 | * 字面表达式(literal expression) 32 | 33 | ```rust 34 | (); // unit type 35 | "hello"; // string type 36 | '1'; // character type 37 | 15; // integer type 38 | ``` 39 | 40 | * 元组表达式(Tuple expression): 41 | 42 | ```rust 43 | (0.0, 4.5); 44 | ("a", 4usize, true); 45 | ``` 46 | 47 | 通常不使用一个元素的元组,不过如果你坚持的话,rust也是允许的,不过需要在元素后加一个逗号: 48 | 49 | ```rust 50 | (0,); // single-element tuple 51 | (0); // zero in parentheses 52 | ``` 53 | 54 | * 结构体表达式(structure expression) 55 | 由于结构体有多种形式,所以结构体表达式也有多种形式。 56 | 57 | ```rust 58 | Point {x: 10.0, y: 20.0}; 59 | TuplePoint(10.0, 20.0); 60 | let u = game::User {name: "Joe", age: 35, score: 100_000}; 61 | some_fn::(Cookie); 62 | ``` 63 | 64 | 结构体表达式一般用于构造一个结构体对象,它除了以上从零构建的形式外,还可以在另一个对象的基础上进行构建: 65 | 66 | ```rust 67 | let base = Point3d {x: 1, y: 2, z: 3}; 68 | Point3d {y: 0, z: 10, .. base}; 69 | ``` 70 | 71 | * 块表达式(block expression): 72 | 块表达式就是用花括号`{}`括起来的一组表达式的集合,表达式间一般以分号分隔。块表达式的值,就是最后一个表达式的值。 73 | 74 | ```rust 75 | let x: i32 = { println!("Hello."); 5 }; 76 | ``` 77 | 78 | 如果以语句结尾,则块表达式的值为`()`: 79 | 80 | ```rust 81 | let x: () = { println!("Hello."); }; 82 | ``` 83 | 84 | * 范围表达式(range expression): 85 | 可以使用范围操作符`..`来构建范围对象(variant of `std::ops::Range`): 86 | 87 | ```rust 88 | 1..2; // std::ops::Range 89 | 3..; // std::ops::RangeFrom 90 | ..4; // std::ops::RangeTo 91 | ..; // std::ops::RangeFull 92 | ``` 93 | 94 | * if表达式(if expression): 95 | 96 | ```rust 97 | let a = 9; 98 | let b = if a%2 == 0 {"even"} else {"odd"}; 99 | ``` 100 | 101 | * 除了以上这些外,还有许多,如: 102 | + path expression 103 | + mehond-call expression 104 | + field expression 105 | + array expression 106 | + index expression 107 | + unary operator expression 108 | + binary operator expression 109 | + return expression 110 | + grouped expression 111 | + match expression 112 | + if expression 113 | + lambda expression 114 | + ... ... 115 | 116 | 这里无法详细展开,读者可以到[Rust Reference][1]去查看。 117 | [1]:http://doc.rust-lang.org/reference.html#statements-and-expressions 118 | 119 | > #### 以上表达式语句中的部分例子引用自[Rust Reference][ref] 120 | [ref]:http://doc.rust-lang.org/reference.html 121 | -------------------------------------------------------------------------------- /quickstart/primitive-type.md: -------------------------------------------------------------------------------- 1 | # 变量绑定与原生类型 2 | 3 | ## 变量绑定 4 | Rust 通过 let 关键字进行变量绑定。 5 | 6 | ```rust 7 | fn main() { 8 | let a1 = 5; 9 | let a2:i32 = 5; 10 | assert_eq!(a1, a2); 11 | //let 绑定 整数变量默认类型推断是 i32 12 | 13 | let b1:u32 = 5; 14 | //assert_eq!(a1, b1); 15 | //去掉上面的注释会报错,因为类型不匹配 16 | //errer: mismatched types 17 | } 18 | ``` 19 | 20 | 这里的 assert_eq! 宏的作用是判断两个参数是不是相等的,但如果是两个不匹配的类型,就算字面值相等也会报错。 21 | 22 | ## 可变绑定 23 | rust 在声明变量时,在变量前面加入 mut 关键字,变量就会成为可变绑定的变量。 24 | 25 | ```rust 26 | fn main() { 27 | let mut a: f64 = 1.0; 28 | let b = 2.0f32; 29 | 30 | //改变 a 的绑定 31 | a = 2.0; 32 | println!("{:?}", a); 33 | 34 | //重新绑定为不可变 35 | let a = a; 36 | 37 | //不能赋值 38 | //a = 3.0; 39 | 40 | //类型不匹配 41 | //assert_eq!(a, b); 42 | } 43 | ``` 44 | 45 | 这里的 b 变量,绑定了 2.0f32。这是 Rust 里面值类型显式标记的语法,规定为`value`+`type`的形式。 46 | 47 | **例如:** 48 | 固定大小类型: 49 | > 1u8 1i8 50 | > 1u16 1i16 51 | > 1u32 1i32 52 | > 1u64 1i64 53 | 54 | 可变大小类型: 55 | > 1usize 1isize 56 | 57 | 浮点类型: 58 | > 1f32 1f64 59 | 60 | ## let解构 61 | 为什么在 Rust 里面声明一个变量的时候要采用 let 绑定表达式? 62 | 那是因为 let 绑定表达式的表达能力更强,而且 let 表达式实际上是一种模式匹配。 63 | 64 | **例如:** 65 | 66 | ```rust 67 | fn main() { 68 | let (a, mut b): (bool,bool) = (true, false); 69 | println!("a = {:?}, b = {:?}", a, b); 70 | //a 不可变绑定 71 | //a = false; 72 | 73 | //b 可变绑定 74 | b = true; 75 | assert_eq!(a, b); 76 | } 77 | ``` 78 | 79 | 这里使用了 bool,只有true和false两个值,通常用来做逻辑判断的类型。 80 | 81 | ## 原生类型 82 | 83 | Rust内置的原生类型 (primitive types) 有以下几类: 84 | 85 | * 布尔类型:有两个值`true`和`false`。 86 | * 字符类型:表示单个Unicode字符,存储为4个字节。 87 | * 数值类型:分为有符号整数 (`i8`, `i16`, `i32`, `i64`, `isize`)、 88 | 无符号整数 (`u8`, `u16`, `u32`, `u64`, `usize`) 以及浮点数 (`f32`, `f64`)。 89 | * 字符串类型:最底层的是不定长类型`str`,更常用的是字符串切片`&str`和堆分配字符串`String`, 90 | 其中字符串切片是静态分配的,有固定的大小,并且不可变,而堆分配字符串是可变的。 91 | * 数组:具有固定大小,并且元素都是同种类型,可表示为`[T; N]`。 92 | * 切片:引用一个数组的部分数据并且不需要拷贝,可表示为`&[T]`。 93 | * 元组:具有固定大小的有序列表,每个元素都有自己的类型,通过解构或者索引来获得每个元素的值。 94 | * 指针:最底层的是裸指针`*const T`和`*mut T`,但解引用它们是不安全的,必须放到`unsafe`块里。 95 | * 函数:具有函数类型的变量实质上是一个函数指针。 96 | * 元类型:即`()`,其唯一的值也是`()`。 97 | 98 | ```rust 99 | // boolean type 100 | let t = true; 101 | let f: bool = false; 102 | 103 | // char type 104 | let c = 'c'; 105 | 106 | // numeric types 107 | let x = 42; 108 | let y: u32 = 123_456; 109 | let z: f64 = 1.23e+2; 110 | let zero = z.abs_sub(123.4); 111 | let bin = 0b1111_0000; 112 | let oct = 0o7320_1546; 113 | let hex = 0xf23a_b049; 114 | 115 | // string types 116 | let str = "Hello, world!"; 117 | let mut string = str.to_string(); 118 | 119 | // arrays and slices 120 | let a = [0, 1, 2, 3, 4]; 121 | let middle = &a[1..4]; 122 | let mut ten_zeros: [i64; 10] = [0; 10]; 123 | 124 | // tuples 125 | let tuple: (i32, &str) = (50, "hello"); 126 | let (fifty, _) = tuple; 127 | let hello = tuple.1; 128 | 129 | // raw pointers 130 | let x = 5; 131 | let raw = &x as *const i32; 132 | let points_at = unsafe { *raw }; 133 | 134 | // functions 135 | fn foo(x: i32) -> i32 { x } 136 | let bar: fn(i32) -> i32 = foo; 137 | ``` 138 | 139 | 有几点是需要特别注意的: 140 | 141 | * 数值类型可以使用`_`分隔符来增加可读性。 142 | * Rust还支持单字节字符`b'H'`以及单字节字符串`b"Hello"`,仅限制于ASCII字符。 143 | 此外,还可以使用`r#"..."#`标记来表示原始字符串,不需要对特殊字符进行转义。 144 | * 使用`&`符号将`String`类型转换成`&str`类型很廉价, 145 | 但是使用`to_string()`方法将`&str`转换到`String`类型涉及到分配内存, 146 | 除非很有必要否则不要这么做。 147 | * 数组的长度是不可变的,动态的数组称为Vec (vector),可以使用宏`vec!`创建。 148 | * 元组可以使用`==`和`!=`运算符来判断是否相同。 149 | * 不多于32个元素的数组和不多于12个元素的元组在值传递时是自动复制的。 150 | * Rust不提供原生类型之间的隐式转换,只能使用`as`关键字显式转换。 151 | * 可以使用`type`关键字定义某个类型的别名,并且应该采用驼峰命名法。 152 | 153 | ```rust 154 | // explicit conversion 155 | let decimal = 65.4321_f32; 156 | let integer = decimal as u8; 157 | let character = integer as char; 158 | 159 | // type aliases 160 | type NanoSecond = u64; 161 | type Point = (u8, u8); 162 | ``` 163 | -------------------------------------------------------------------------------- /rcarc/rcarc.md: -------------------------------------------------------------------------------- 1 | # Rc 和 Arc 2 | 3 | Rust 建立在所有权之上的这一套机制,它要求一个资源同一时刻有且只能有一个拥有所有权的绑定或 `&mut` 引用,这在大部分的情况下保证了内存的安全。但是这样的设计是相当严格的,在另外一些情况下,它限制了程序的书写,无法实现某些功能。因此,Rust 在 std 库中提供了额外的措施来补充所有权机制,以应对更广泛的场景。 4 | 5 | 默认 Rust 中,对一个资源,同一时刻,有且只有一个所有权拥有者。`Rc` 和 `Arc` 使用引用计数的方法,让程序在同一时刻,实现同一资源的多个所有权拥有者,多个拥有者共享资源。 6 | 7 | ## Rc 8 | `Rc` 用于同一线程内部,通过 `use std::rc::Rc` 来引入。它有以下几个特点: 9 | 10 | 1. 用 `Rc` 包装起来的类型对象,是 `immutable` 的,即 不可变的。即你无法修改 `Rc` 中的 `T` 对象,只能读; 11 | 2. 一旦最后一个拥有者消失,则资源会被自动回收,这个生命周期是在编译期就确定下来的; 12 | 3. `Rc` 只能用于同一线程内部,不能用于线程之间的对象共享(不能跨线程传递); 13 | 4. `Rc` 实际上是一个指针,它不影响包裹对象的方法调用形式(即不存在先解开包裹再调用值这一说)。 14 | 15 | 例子: 16 | 17 | ```rust 18 | use std::rc::Rc; 19 | 20 | let five = Rc::new(5); 21 | let five2 = five.clone(); 22 | let five3 = five.clone(); 23 | 24 | ``` 25 | 26 | ## Rc Weak 27 | 28 | `Weak` 通过 `use std::rc::Weak` 来引入。 29 | 30 | `Rc` 是一个引用计数指针,而 `Weak` 是一个指针,但不增加引用计数,是 `Rc` 的 weak 版。它有以下几个特点: 31 | 32 | 1. 可访问,但不拥有。不增加引用计数,因此,不会对资源回收管理造成影响; 33 | 2. 可由 `Rc` 调用 `downgrade` 方法而转换成 `Weak`; 34 | 3. `Weak` 可以使用 `upgrade` 方法转换成 `Option>`,如果资源已经被释放,则 Option 值为 `None`; 35 | 4. 常用于解决循环引用的问题。 36 | 37 | 例子: 38 | 39 | ```rust 40 | use std::rc::Rc; 41 | 42 | let five = Rc::new(5); 43 | 44 | let weak_five = Rc::downgrade(&five); 45 | 46 | let strong_five: Option> = weak_five.upgrade(); 47 | ``` 48 | 49 | ## Arc 50 | 51 | `Arc` 是原子引用计数,是 `Rc` 的多线程版本。`Arc` 通过 `std::sync::Arc` 引入。 52 | 53 | 它的特点: 54 | 55 | 1. `Arc` 可跨线程传递,用于跨线程共享一个对象; 56 | 2. 用 `Arc` 包裹起来的类型对象,对可变性没有要求; 57 | 3. 一旦最后一个拥有者消失,则资源会被自动回收,这个生命周期是在编译期就确定下来的; 58 | 4. `Arc` 实际上是一个指针,它不影响包裹对象的方法调用形式(即不存在先解开包裹再调用值这一说); 59 | 5. `Arc` 对于多线程的共享状态**几乎是必须的**(减少复制,提高性能)。 60 | 61 | 示例: 62 | 63 | ```rust 64 | use std::sync::Arc; 65 | use std::thread; 66 | 67 | fn main() { 68 | let numbers: Vec<_> = (0..100u32).collect(); 69 | let shared_numbers = Arc::new(numbers); 70 | 71 | for _ in 0..10 { 72 | let child_numbers = shared_numbers.clone(); 73 | 74 | thread::spawn(move || { 75 | let local_numbers = &child_numbers[..]; 76 | 77 | // Work with the local numbers 78 | }); 79 | } 80 | } 81 | ``` 82 | 83 | ### Arc Weak 84 | 85 | 与 `Rc` 类似,`Arc` 也有一个对应的 `Weak` 类型,从 `std::sync::Weak` 引入。 86 | 87 | 意义与用法与 `Rc Weak` 基本一致,不同的点是这是多线程的版本。故不再赘述。 88 | 89 | 90 | 91 | ## 一个例子 92 | 93 | 下面这个例子,表述的是如何实现多个对象同时引用另外一个对象。 94 | 95 | ```rust 96 | use std::rc::Rc; 97 | 98 | struct Owner { 99 | name: String 100 | } 101 | 102 | struct Gadget { 103 | id: i32, 104 | owner: Rc 105 | } 106 | 107 | fn main() { 108 | // Create a reference counted Owner. 109 | let gadget_owner : Rc = Rc::new( 110 | Owner { name: String::from("Gadget Man") } 111 | ); 112 | 113 | // Create Gadgets belonging to gadget_owner. To increment the reference 114 | // count we clone the `Rc` object. 115 | let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() }; 116 | let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() }; 117 | 118 | drop(gadget_owner); 119 | 120 | // Despite dropping gadget_owner, we're still able to print out the name 121 | // of the Owner of the Gadgets. This is because we've only dropped the 122 | // reference count object, not the Owner it wraps. As long as there are 123 | // other `Rc` objects pointing at the same Owner, it will remain 124 | // allocated. Notice that the `Rc` wrapper around Gadget.owner gets 125 | // automatically dereferenced for us. 126 | println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); 127 | println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); 128 | 129 | // At the end of the method, gadget1 and gadget2 get destroyed, and with 130 | // them the last counted references to our Owner. Gadget Man now gets 131 | // destroyed as well. 132 | } 133 | ``` 134 | -------------------------------------------------------------------------------- /concurrency-parallel-thread/share-memory.md: -------------------------------------------------------------------------------- 1 | ## 共享内存 2 | 在消息传递之外,还存在一种广为人知的并发模型,那就是共享内存。其实如果不能共享内存,消息传递也是不能在不同的线程间传递消息,也谈不上在不同的线程间等待和通知了。共享内存是这一切得以发生的基础。如果查看源码,你会发现消息传递的内部实现就是借用了共享内存机制。相对于消息传递而言,共享内存会有更多的竞争,但是不用进行多次拷贝,在某些情况下,也需要考虑使用这种方式来处理。在Rust中,能共享内存的情况,主要体现在下面两个方面: 3 | 4 | ### static 5 | Rust语言中也存在static变量,其生命周期是整个应用程序,并且在内存中某个固定地址处只存在一份实例。所有线程都能够访问到它。这种方式也是最简单和直接的共享方式。几乎大多数语言都存在这种机制。下面简单看一下Rust中多个线程访问static变量的用法: 6 | 7 | ```rust 8 | use std::thread; 9 | 10 | static VAR: i32 = 5; 11 | 12 | fn main() { 13 | // 创建一个新线程 14 | let new_thread = thread::spawn(move|| { 15 | println!("static value in new thread: {}", VAR); 16 | }); 17 | 18 | // 等待新线程先运行 19 | new_thread.join().unwrap(); 20 | println!("static value in main thread: {}", VAR); 21 | } 22 | ``` 23 | 24 | 运行结果: 25 | 26 | ``` 27 | static value in new thread: 5 28 | static value in main thread: 5 29 | ``` 30 | 31 | `VAR`这个`static`变量在各线程中可以直接使用,非常方便。当然上面只是读取,那么要修改也是很简单的: 32 | 33 | ```rust 34 | use std::thread; 35 | 36 | static mut VAR: i32 = 5; 37 | 38 | fn main() { 39 | // 创建一个新线程 40 | let new_thread = thread::spawn(move|| { 41 | unsafe { 42 | println!("static value in new thread: {}", VAR); 43 | VAR = VAR + 1; 44 | } 45 | }); 46 | 47 | // 等待新线程先运行 48 | new_thread.join().unwrap(); 49 | unsafe { 50 | println!("static value in main thread: {}", VAR); 51 | } 52 | } 53 | ``` 54 | 55 | 运行结果: 56 | 57 | ``` 58 | static value in new thread: 5 59 | static value in main thread: 6 60 | ``` 61 | 62 | 从结果来看`VAR`的值变了,从代码上来看,除了在`VAR`变量前面加了`mut`关键字外,更加明显的是在使用`VAR`的地方都添加了`unsafe`代码块。为什么?所有的线程都能访问`VAR`,且它是可以被修改的,自然就是不安全的。上面的代码比较简单,同一时间只会有一个线程读写`VAR`,不会有什么问题,所以用`unsafe`来标记就可以。如果是更多的线程,还是请使用接下来要介绍的同步机制来处理。 63 | 64 | static如此,那const呢? const会在编译时内联到代码中,所以不会存在某个固定的内存地址上,也不存在可以修改的情况,并不是内存共享的。 65 | 66 | ### 堆 67 | 由于现代操作系统的设计,线程寄生于进程,可以共享进程的资源,如果要在各个线程中共享一个变量,那么除了上面的static,还有就是把变量保存在堆上了。当然Rust也不例外,遵从这一设计。只是我们知道Rust在安全性上肯定又会做一些考量,从而在语言设计和使用上稍有不同。 68 | 69 | 为了在堆上分配空间,Rust提供了`std::boxed::Box`,由于堆的特点,存活时间比较长,所以除了我们这个地方介绍的线程间共享外,还有其他的用处,此处不详细说明,若不甚了解,请学习或回顾**堆、栈与Box**章节的介绍。下面我们来看一下如何在多个线程间访问`Box`创建的变量: 70 | 71 | ```rust 72 | use std::thread; 73 | use std::sync::Arc; 74 | 75 | fn main() { 76 | let var : Arc = Arc::new(5); 77 | let share_var = var.clone(); 78 | 79 | // 创建一个新线程 80 | let new_thread = thread::spawn(move|| { 81 | println!("share value in new thread: {}, address: {:p}", share_var, &*share_var); 82 | }); 83 | 84 | // 等待新建线程先执行 85 | new_thread.join().unwrap(); 86 | println!("share value in main thread: {}, address: {:p}", var, &*var); 87 | } 88 | ``` 89 | 90 | 运行结果: 91 | 92 | ``` 93 | share value in new thread: 5, address: 0x2825070 94 | share value in main thread: 5, address: 0x2825070 95 | ``` 96 | 97 | 你可能会觉得很奇怪,上面怎么没有看到Box创建的变量啊,这明明就是`Arc`的使用呀?`Box`创建的变量要想在多个线程中安全使用,我们还需要实现很多功能才行,需要是`Sync`,而`Arc`正是利用`Box`来实现的一个通过引用计数来共享状态的包裹类。下面引用一段`Arc::new`的源码即可看出它是通过`Box`来实现的: 98 | 99 | ```rust 100 | pub fn new(data: T) -> Arc { 101 | // Start the weak pointer count as 1 which is the weak pointer that's 102 | // held by all the strong pointers (kinda), see std/rc.rs for more info 103 | let x: Box<_> = box ArcInner { 104 | strong: atomic::AtomicUsize::new(1), 105 | weak: atomic::AtomicUsize::new(1), 106 | data: data, 107 | }; 108 | Arc { _ptr: unsafe { NonZero::new(Box::into_raw(x)) } } 109 | } 110 | ``` 111 | 112 | 通过上面的运行结果,我们也可以发现新建线程和主线程中打印的`address`是一样的,说明状态确实是在同一个内存地址处。 113 | 114 | 如果`Box`在堆上分配的资源仅在一个线程中使用,那么释放时,就非常简单,使用完,及时释放即可。如果是要在多个线程中使用,就需要面临两个关键问题: 115 | 116 | 1. 资源何时释放? 117 | 2. 线程如何安全的并发修改和读取? 118 | 119 | 由于上面两个问题的存在,这就是为什么我们不能直接用`Box`变量在线程中共享的原因,可以看出来,共享内存比消息传递机制似乎要复杂许多。Rust用了引用计数的方式来解决第一个问题,在标准库中提供了两个包裹类,除了上面一个用于多线程的`std::sync::Arc`之外,还有一个不能用于多线程的`std::rc::Rc`。在使用时,可以根据需要进行选择。如果你一不小心把`std::rc::Rc`用于多线程中,编译器会毫不客气地纠正你的。 120 | 121 | 关于上面的第二个问题,Rust语言及标准库提供了一系列的同步手段来解决。下面的章节我们将详细讲解这些方式和用法。 122 | -------------------------------------------------------------------------------- /function/higher_order_function.md: -------------------------------------------------------------------------------- 1 | # 高阶函数 2 | 高阶函数与普通函数的不同在于,它可以使用一个或多个函数作为参数,可以将函数作为返回值。rust的函数是first class type,所以支持高阶函数。而,由于rust是一个强类型的语言,如果要将函数作为参数或返回值,首先需要搞明白函数的类型。下面先说函数的类型,再说函数作为参数和返回值。 3 | 4 | ## 函数类型 5 | 前面说过,关键字`fn`可以用来定义函数。除此以外,它还用来构造函数类型。与函数定义主要的不同是,构造函数类型不需要函数名、参数名和函数体。在Rust Reference中的描述如下: 6 | > The function type constructor fn forms new function types. A function type consists of a possibly-empty set of function-type modifiers (such as unsafe or extern), a sequence of input types and an output type. 7 | 8 | 来看一个简单例子: 9 | 10 | ```rust 11 | fn inc(n: i32) -> i32 {//函数定义 12 | n + 1 13 | } 14 | 15 | type IncType = fn(i32) -> i32;//函数类型 16 | 17 | fn main() { 18 | let func: IncType = inc; 19 | println!("3 + 1 = {}", func(3)); 20 | } 21 | ``` 22 | 23 | 上例首先使用`fn`定义了`inc`函数,它有一个`i32`类型参数,返回`i32`类型的值。然后再用`fn`定义了一个函数类型,这个函数类型有i32类型的参数和i32类型的返回值,并用`type`关键字定义了它的别名`IncType`。在`main`函数中定义了一个变量`func`,其类型就为`IncType`,并赋值为`inc`,然后在`pirntln`宏中调用:`func(3)`。可以看到,`inc`函数的类型其实就是`IncType`。 24 | 这里有一个问题,我们将`inc`赋值给了`func`,而不是`&inc`,这样是将`inc`函数的拥有权转给了`func`吗,赋值后还可以以`inc()`形式调用`inc`函数吗?先来看一个例子: 25 | 26 | ```rust 27 | fn main() { 28 | let func: IncType = inc; 29 | println!("3 + 1 = {}", func(3)); 30 | println!("3 + 1 = {}", inc(3)); 31 | } 32 | 33 | type IncType = fn(i32) -> i32; 34 | 35 | fn inc(n: i32) -> i32 { 36 | n + 1 37 | } 38 | ``` 39 | 40 | 我们将上例保存在rs源文件中,再用rustc编译,发现并没有报错,并且运行也得到我们想要的结果: 41 | 42 | ``` 43 | 3 + 1 = 4 44 | 3 + 1 = 4 45 | ``` 46 | 47 | 这说明,赋值时,`inc`函数的所有权并没有被转移到`func`变量上,而是更像不可变引用。在rust中,函数的所有权是不能转移的,我们给函数类型的变量赋值时,赋给的一般是函数的指针,所以rust中的函数类型,就像是C/C++中的函数指针,当然,rust的函数类型更安全。可见,rust的函数类型,其实应该是属于指针类型(Pointer Type)。rust的Pointer Type有两种,一种为引用(Reference`&`),另一种为原始指针(Raw pointer `*`),详细内容请看[Rust Reference 8.18 Pointer Types](http://doc.rust-lang.org/reference.html#pointer-types)。而rust的函数类型应是引用类型,因为它是安全的,而原始指针则是不安全的,要使用原始指针,必须使用`unsafe`关键字声明。 48 | 49 | ## 函数作为参数 50 | 函数作为参数,其声明与普通参数一样。看下例: 51 | 52 | ```rust 53 | fn main() { 54 | println!("3 + 1 = {}", process(3, inc)); 55 | println!("3 - 1 = {}", process(3, dec)); 56 | } 57 | 58 | fn inc(n: i32) -> i32 { 59 | n + 1 60 | } 61 | 62 | fn dec(n: i32) -> i32 { 63 | n - 1 64 | } 65 | 66 | fn process(n: i32, func: fn(i32) -> i32) -> i32 { 67 | func(n) 68 | } 69 | ``` 70 | 71 | 例子中,`process`就是一个高阶函数,它有两个参数,一个类型为`i32`的`n`,另一个类型为`fn(i32)->i32`的函数`func`,返回一个`i32`类型的参数;它在函数体内以`n`作为参数调用`func`函数,返回`func`函数的返回值。运行可以得到以下结果: 72 | 73 | ``` 74 | 3 + 1 = 4 75 | 3 - 1 = 2 76 | ``` 77 | 78 | 不过,这不是函数作为参数的唯一声明方法,使用泛型函数配合特质(`trait`)也是可以的,因为rust的函数都会实现一个`trait`:`FnOnce`、`Fn`或`FnMut`。将上例中的`process`函数定义换成以下形式是等价的: 79 | 80 | ```rust 81 | fn process(n: i32, func: F) -> i32 82 | where F: Fn(i32) -> i32 { 83 | func(n) 84 | } 85 | ``` 86 | 87 | ## 函数作为返回值 88 | 函数作为返回值,其声明与普通函数的返回值类型声明一样。看例子: 89 | 90 | ```rust 91 | fn main() { 92 | let a = [1,2,3,4,5,6,7]; 93 | let mut b = Vec::::new(); 94 | for i in &a { 95 | b.push(get_func(*i)(*i)); 96 | } 97 | println!("{:?}", b); 98 | } 99 | 100 | fn get_func(n: i32) -> fn(i32) -> i32 { 101 | fn inc(n: i32) -> i32 { 102 | n + 1 103 | } 104 | fn dec(n: i32) -> i32 { 105 | n - 1 106 | } 107 | if n % 2 == 0 { 108 | inc 109 | } else { 110 | dec 111 | } 112 | } 113 | ``` 114 | 115 | 例子中的高阶函数为`get_func`,它接收一个i32类型的参数,返回一个类型为`fn(i32) -> i32`的函数,若传入的参数为偶数,返回`inc`,否则返回`dec`。这里需要注意的是,`inc`函数和`dec`函数都定义在`get_func`内。在函数内定义函数在很多其他语言中是不支持的,不过rust支持,这也是rust灵活和强大的一个体现。不过,在函数中定义的函数,不能包含函数中(环境中)的变量,若要包含,应该闭包(详看13章 闭包)。 116 | 所以下例: 117 | 118 | ```rust 119 | fn main() { 120 | let f = get_func(); 121 | println!("{}", f(3)); 122 | } 123 | 124 | fn get_func() -> fn(i32)->i32 { 125 | let a = 1; 126 | fn inc(n:i32) -> i32 { 127 | n + a 128 | } 129 | inc 130 | } 131 | ``` 132 | 133 | 使用rustc编译,会出现如下错误: 134 | ![error](../images/high-order-function.png) 135 | -------------------------------------------------------------------------------- /trait/trait-object.md: -------------------------------------------------------------------------------- 1 | # trait对象 (trait object) 2 | 3 | trait对象在**Rust**中是指使用指针封装了的 trait,比如 `&SomeTrait` 和 `Box`。 4 | 5 | ```rust 6 | trait Foo { fn method(&self) -> String; } 7 | 8 | impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } 9 | impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } 10 | 11 | fn do_something(x: &Foo) { 12 | x.method(); 13 | } 14 | 15 | fn main() { 16 | let x = "Hello".to_string(); 17 | do_something(&x); 18 | let y = 8u8; 19 | do_something(&y); 20 | } 21 | ``` 22 | 23 | `x: &Foo`其中`x`是一个trait对象,这里用指针是因为`x`可以是任意实现`Foo`的类型实例,内存大小并不确定,但指针的大小是固定的。 24 | 25 | ## trait对象的实现 26 | 27 | `&SomeTrait` 类型和普通的指针类型`&i32`不同。它不仅包括指向真实对象的指针,还包括一个指向虚函数表的指针。它的内部实现定义在在`std::raw`模块中: 28 | 29 | ```rust 30 | pub struct TraitObject { 31 | pub data: *mut (), 32 | pub vtable: *mut (), 33 | } 34 | ``` 35 | 36 | 其中`data`是一个指向实际类型实例的指针, `vtable`是一个指向实际类型对于该trait的实现的虚函数表: 37 | 38 | `Foo`的虚函数表类型: 39 | 40 | ```rust 41 | struct FooVtable { 42 | destructor: fn(*mut ()), 43 | size: usize, 44 | align: usize, 45 | method: fn(*const ()) -> String, 46 | } 47 | ``` 48 | 49 | 之前的代码可以解读为: 50 | 51 | ```rust 52 | // u8: 53 | // 这个函数只会被指向u8的指针调用 54 | fn call_method_on_u8(x: *const ()) -> String { 55 | let byte: &u8 = unsafe { &*(x as *const u8) }; 56 | 57 | byte.method() 58 | } 59 | 60 | static Foo_for_u8_vtable: FooVtable = FooVtable { 61 | destructor: /* compiler magic */, 62 | size: 1, 63 | align: 1, 64 | 65 | method: call_method_on_u8 as fn(*const ()) -> String, 66 | }; 67 | 68 | 69 | // String: 70 | // 这个函数只会被指向String的指针调用 71 | fn call_method_on_String(x: *const ()) -> String { 72 | let string: &String = unsafe { &*(x as *const String) }; 73 | 74 | string.method() 75 | } 76 | 77 | static Foo_for_String_vtable: FooVtable = FooVtable { 78 | destructor: /* compiler magic */, 79 | size: 24, 80 | align: 8, 81 | 82 | method: call_method_on_String as fn(*const ()) -> String, 83 | }; 84 | 85 | 86 | let a: String = "foo".to_string(); 87 | let x: u8 = 1; 88 | 89 | // let b: &Foo = &a; 90 | let b = TraitObject { 91 | // data存储实际值的引用 92 | data: &a, 93 | // vtable存储实际类型实现Foo的方法 94 | vtable: &Foo_for_String_vtable 95 | }; 96 | 97 | // let y: &Foo = x; 98 | let y = TraitObject { 99 | data: &x, 100 | vtable: &Foo_for_u8_vtable 101 | }; 102 | 103 | // b.method(); 104 | (b.vtable.method)(b.data); 105 | 106 | // y.method(); 107 | (y.vtable.method)(y.data); 108 | ``` 109 | 110 | ## 对象安全 111 | 112 | 并不是所有的trait都能作为trait对象使用的,比如: 113 | 114 | ```rust 115 | let v = vec![1, 2, 3]; 116 | let o = &v as &Clone; 117 | ``` 118 | 119 | 会有一个错误: 120 | 121 | ``` 122 | error: cannot convert to a trait object because trait `core::clone::Clone` is not object-safe [E0038] 123 | let o = &v as &Clone; 124 | ^~ 125 | note: the trait cannot require that `Self : Sized` 126 | let o = &v as &Clone; 127 | ^~ 128 | ``` 129 | 让我来分析一下错误的原因: 130 | 131 | ```rust 132 | pub trait Clone: Sized { 133 | fn clone(&self) -> Self; 134 | 135 | fn clone_from(&mut self, source: &Self) { ... } 136 | } 137 | ``` 138 | 139 | 虽然`Clone`本身继承了`Sized`这个trait,但是它的方法`fn clone(&self) -> Self`和`fn clone_from(&mut self, source: &Self) { ... }`含有`Self`类型,而在使用trait对象方法的时候**Rust**是动态派发的,我们根本不知道这个trait对象的实际类型,它可以是任何一个实现了该trait的类型的值,所以`Self`在这里的大小不是`Self: Sized`的,这样的情况在**Rust**中被称为`object-unsafe`或者`not object-safe`,这样的trait是不能成为trait对象的。 140 | 141 | 总结: 142 | 143 | 如果一个`trait`方法是`object safe`的,它需要满足: 144 | 145 | * 方法有`Self: Sized`约束, 或者 146 | * 同时满足以下所有条件: 147 | * 没有泛型参数 148 | * 不是静态函数 149 | * 除了`self`之外的其它参数和返回值不能使用`Self`类型 150 | 151 | 如果一个`trait`是`object-safe`的,它需要满足: 152 | 153 | * 所有的方法都是`object-safe`的,并且 154 | * trait 不要求 `Self: Sized` 约束 155 | 156 | 参考[stackoverflow](http://stackoverflow.com/questions/29985153/trait-object-is-not-object-safe-error) 157 | [object safe rfc](https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md) 158 | -------------------------------------------------------------------------------- /action/json_data/readme.md: -------------------------------------------------------------------------------- 1 | # Rust json处理 2 | 3 | JSON是一种比较重要的格式,尤其是现在的web开发领域,JSON相比于传统的XML更加容易操作和减小传输。 4 | 5 | Rust中的JSON处理依赖 cargo 中的rustc-serialize模块 6 | 7 | ###先简单的创建一个Rust项目工程 8 | 9 | ``` rust 10 | $ cargo new json_data --bin 11 | ``` 12 | 13 | 生成文件树: 14 | 15 | ```shell 16 | vagrant@ubuntu-14:~/tmp/test/rustprimer$ tree 17 | . 18 | `-- json_data 19 | |-- Cargo.toml 20 | `-- src 21 | `-- main.rs 22 | 23 | 24 | ``` 25 | 26 | 生成项目`json_data`,项目下文件介绍: 27 | 28 | - Caogo.toml ,文件中填写一些项目的相关信息,比如版本号,联系人,项目名,文件的内容如下: 29 | 30 | ```toml 31 | [package] 32 | name = "json_data" 33 | version = "0.1.0" 34 | authors = ["wangxxx "] 35 | 36 | [dependencies] 37 | 38 | ``` 39 | 40 | - src 中放置项目的源代码,main.rs 为项目的入口文件。 41 | 42 | ###一些必要的了解 43 | 44 | rustc-serialize 这个是第三方的模块,需要从[cargo](https://crates.io/crates/rustc-serialize)下载。 45 | 下载很简单,只需修改一下cargo.toml文件就行了. 46 | 47 | ```toml 48 | [package] 49 | name = "json_data" 50 | version = "0.1.0" 51 | authors = ["wangxxx "] 52 | 53 | [dependencies] 54 | rustc-serialize = "0.3.18" 55 | 56 | ``` 57 | 58 | 然后执行在当前目录执行: 59 | 60 | ``` 61 | $ cargo build 62 | ``` 63 | 64 | *注意一个问题由于国内网络访问github不稳定,这些第三方库很多托管在github上,所以可能需要修改你的 65 | 网络访问* 66 | 67 | 1. 在安装Rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹 68 | 2. 在`.cargo`文件夹下,创建一个`config`文件,在文件中填写中科大软件源,可能以后会出现其他的源,先用这个 69 | 3. `config`文件内容如下 70 | 71 | ```toml 72 | [registry] 73 | index = "git://crates.mirrors.ustc.edu.cn/index" 74 | 75 | ``` 76 | 77 | cargo build 执行之后的提示信息 78 | 79 | ``` 80 | Updating registry `git://crates.mirrors.ustc.edu.cn/index` 81 | Downloading rustc-serialize v0.3.18 (registry git://crates.mirrors.ustc.edu.cn/index) 82 | Compiling rustc-serialize v0.3.18 (registry git://crates.mirrors.ustc.edu.cn/index) 83 | Compiling json_data v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/json_data) 84 | ``` 85 | 86 | 再次执行tree命令: 87 | 88 | ``` 89 | . 90 | |-- Cargo.lock 91 | |-- Cargo.toml 92 | |-- src 93 | | `-- main.rs 94 | `-- target 95 | `-- debug 96 | |-- build 97 | |-- deps 98 | | `-- librustc_serialize-d27006e102b906b6.rlib 99 | |-- examples 100 | |-- json_data 101 | `-- native 102 | 103 | ``` 104 | 105 | 可以看到多了很多文件,重点关注`cargo.lock`,开打文件: 106 | 107 | ```toml 108 | [root] 109 | name = "json_data" 110 | version = "0.1.0" 111 | dependencies = [ 112 | "rustc-serialize 0.3.18 (registry+git://crates.mirrors.ustc.edu.cn/index)", 113 | ] 114 | 115 | [[package]] 116 | name = "rustc-serialize" 117 | version = "0.3.18" 118 | source = "registry+git://crates.mirrors.ustc.edu.cn/index" 119 | 120 | ``` 121 | 122 | 是关于项目编译的一些依赖信息 123 | 124 | 还有生成了target文件夹,生成了可执行文件json_data,因为main.rs中的执行结果就是打印`hello world` 125 | 126 | ``` 127 | $ cargo run 128 | 129 | Hello, world! 130 | ``` 131 | 132 | ###开始写代码 133 | 直接使用官方的 [rustc_serialize 中的例子](https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html#using-autoserialization): 134 | 135 | ``` rust 136 | extern crate rustc_serialize; 137 | // 引入rustc_serialize模块 138 | use rustc_serialize::json; 139 | 140 | // Automatically generate `RustcDecodable` and `RustcEncodable` trait 141 | // implementations 142 | // 定义TestStruct 143 | #[derive(RustcDecodable, RustcEncodable)] 144 | pub struct TestStruct { 145 | data_int: u8, 146 | data_str: String, 147 | data_vector: Vec, 148 | } 149 | 150 | fn main() { 151 | // 初始化TestStruct 152 | let object = TestStruct { 153 | data_int: 1, 154 | data_str: "homura".to_string(), 155 | data_vector: vec![2,3,4,5], 156 | }; 157 | 158 | // Serialize using `json::encode` 159 | // 将TestStruct转意为字符串 160 | let encoded = json::encode(&object).unwrap(); 161 | println!("{}",encoded); 162 | // Deserialize using `json::decode` 163 | // 将json字符串中的数据转化成TestStruct对应的数据,相当于初始化 164 | let decoded: TestStruct = json::decode(&encoded).unwrap(); 165 | println!("{:?}",decoded.data_vector); 166 | } 167 | 168 | ``` 169 | 170 | 当然我们也可以在文本中作为api的返回结果使用,下来的章节中,我们将讨论这个问题 171 | -------------------------------------------------------------------------------- /closure/syntax.md: -------------------------------------------------------------------------------- 1 | # 闭包的语法 2 | ## 基本形式 3 | 闭包看起来像这样: 4 | 5 | ```rust 6 | let plus_one = |x: i32| x + 1; 7 | 8 | assert_eq!(2, plus_one(1)); 9 | ``` 10 | 11 | 我们创建了一个绑定,`plus_one`,并把它赋予一个闭包。闭包的参数位于管道(`|`)之中,而闭包体是一个表达式,在这个例子中,`x + 1`。记住`{}`是一个表达式,所以我们也可以拥有包含多行的闭包: 12 | 13 | ```rust 14 | let plus_two = |x| { 15 | let mut result: i32 = x; 16 | 17 | result += 1; 18 | result += 1; 19 | 20 | result 21 | }; 22 | 23 | assert_eq!(4, plus_two(2)); 24 | ``` 25 | 26 | 你会注意到闭包的一些方面与用`fn`定义的常规函数有点不同。第一个是我们并不需要标明闭包接收和返回参数的类型。我们可以: 27 | 28 | ```rust 29 | let plus_one = |x: i32| -> i32 { x + 1 }; 30 | 31 | assert_eq!(2, plus_one(1)); 32 | ``` 33 | 34 | 不过我们并不需要这么写。为什么呢?基本上,这是出于“人体工程学”的原因。因为为命名函数指定全部类型有助于像文档和类型推断,而闭包的类型则很少有文档因为它们是匿名的,并且并不会产生像推断一个命名函数的类型这样的“远距离错误”。 35 | 36 | 第二个的语法大同小异。我会增加空格来使它们看起来更像一点: 37 | 38 | ```rust 39 | fn plus_one_v1 (x: i32) -> i32 { x + 1 } 40 | let plus_one_v2 = |x: i32| -> i32 { x + 1 }; 41 | let plus_one_v3 = |x: i32| x + 1 ; 42 | ``` 43 | 44 | ## 捕获变量 45 | 之所以把它称为“闭包”是因为它们“包含在环境中”(close over their environment)。这看起来像: 46 | 47 | ```rust 48 | let num = 5; 49 | let plus_num = |x: i32| x + num; 50 | 51 | assert_eq!(10, plus_num(5)); 52 | ``` 53 | 54 | 这个闭包,`plus_num`,引用了它作用域中的`let`绑定:`num`。更明确的说,它借用了绑定。如果我们做一些会与这个绑定冲突的事,我们会得到一个错误。比如这个: 55 | 56 | ```rust 57 | let mut num = 5; 58 | let plus_num = |x: i32| x + num; 59 | 60 | let y = &mut num; 61 | ``` 62 | 63 | 错误是: 64 | 65 | ```text 66 | error: cannot borrow `num` as mutable because it is also borrowed as immutable 67 | let y = &mut num; 68 | ^~~ 69 | note: previous borrow of `num` occurs here due to use in closure; the immutable 70 | borrow prevents subsequent moves or mutable borrows of `num` until the borrow 71 | ends 72 | let plus_num = |x| x + num; 73 | ^~~~~~~~~~~ 74 | note: previous borrow ends here 75 | fn main() { 76 | let mut num = 5; 77 | let plus_num = |x| x + num; 78 | 79 | let y = &mut num; 80 | } 81 | ^ 82 | ``` 83 | 84 | 一个啰嗦但有用的错误信息!如它所说,我们不能取得一个`num`的可变借用因为闭包已经借用了它。如果我们让闭包离开作用域,我们可以: 85 | 86 | ```rust 87 | let mut num = 5; 88 | { 89 | let plus_num = |x: i32| x + num; 90 | 91 | } // plus_num goes out of scope, borrow of num ends 92 | 93 | let y = &mut num; 94 | ``` 95 | 96 | 如果你的闭包需要它,Rust会取得所有权并移动环境: 97 | 98 | ```rust 99 | let nums = vec![1, 2, 3]; 100 | 101 | let takes_nums = || nums; 102 | 103 | println!("{:?}", nums); 104 | ``` 105 | 106 | 这会给我们: 107 | 108 | ```text 109 | note: `nums` moved into closure environment here because it has type 110 | `[closure(()) -> collections::vec::Vec]`, which is non-copyable 111 | let takes_nums = || nums; 112 | ^~~~~~~ 113 | ``` 114 | 115 | `Vec`拥有它内容的所有权,而且由于这个原因,当我们在闭包中引用它时,我们必须取得`nums`的所有权。这与我们传递`nums`给一个取得它所有权的函数一样。 116 | 117 | ## move闭包 118 | 我们可以使用`move`关键字强制使我们的闭包取得它环境的所有权: 119 | 120 | ```rust 121 | let num = 5; 122 | 123 | let owns_num = move |x: i32| x + num; 124 | ``` 125 | 126 | 现在,即便关键字是`move`,变量遵循正常的移动语义。在这个例子中,`5`实现了`Copy`,所以`owns_num`取得一个`5`的拷贝的所有权。那么区别是什么呢? 127 | 128 | ```rust 129 | let mut num = 5; 130 | 131 | { 132 | let mut add_num = |x: i32| num += x; 133 | 134 | add_num(5); 135 | } 136 | 137 | assert_eq!(10, num); 138 | ``` 139 | 140 | 那么在这个例子中,我们的闭包取得了一个`num`的可变引用,然后接着我们调用了`add_num`,它改变了其中的值,正如我们期望的。我们也需要将`add_num`声明为`mut`,因为我们会改变它的环境。 141 | 142 | 如果我们加上`move`修饰闭包,会发生些不同: 143 | 144 | ```rust 145 | let mut num = 5; 146 | 147 | { 148 | let mut add_num = move |x: i32| num += x; 149 | 150 | add_num(5); 151 | } 152 | 153 | assert_eq!(5, num); 154 | ``` 155 | 156 | 我们只会得到`5`。这次我们没有获取到外部的`num`的可变借用,我们实际上是把 `num` move 进了闭包。因为 `num` 具有 Copy 属性,因此发生 move 之后,以前的变量生命周期并未结束,还可以继续在 `assert_eq!` 中使用。我们打印的变量和闭包内的变量是独立的两个变量。如果我们捕获的环境变量不是 Copy 的,那么外部环境变量被 move 进闭包后, 157 | 它就不能继续在原先的函数中使用了,只能在闭包内使用。 158 | 159 | 不过在我们讨论获取或返回闭包之前,我们应该更多的了解一下闭包实现的方法。作为一个系统语言,Rust给予你了大量的控制你代码的能力,而闭包也是一样。 160 | 161 | > ### 这部分引用自[The Rust Programming Language中文版](https://github.com/KaiserY/rust-book-chinese/blob/master/content/Closures%20%E9%97%AD%E5%8C%85.md) 162 | -------------------------------------------------------------------------------- /quickstart/control-flow.md: -------------------------------------------------------------------------------- 1 | # 控制流(control flow) 2 | 3 | ## If 4 | 5 | If是分支 (branch) 的一种特殊形式,也可以使用`else`和`else if`。 6 | 与C语言不同的是,逻辑条件不需要用小括号括起来,但是条件后面必须跟一个代码块。 7 | Rust中的`if`是一个表达式 (expression),可以赋给一个变量: 8 | 9 | ```rust 10 | let x = 5; 11 | 12 | let y = if x == 5 { 10 } else { 15 }; 13 | ``` 14 | 15 | Rust是基于表达式的编程语言,有且仅有两种语句 (statement): 16 | 17 | 1. **声明语句** (declaration statement),比如进行变量绑定的`let`语句。 18 | 2. **表达式语句** (expression statement),它通过在末尾加上分号`;`来将表达式变成语句, 19 | 丢弃该表达式的值,一律返回unit`()`。 20 | 21 | 表达式如果返回,总是返回一个值,但是语句不返回值或者返回`()`,所以以下代码会报错: 22 | 23 | ```rust 24 | let y = (let x = 5); 25 | 26 | let z: i32 = if x == 5 { 10; } else { 15; }; 27 | ``` 28 | 29 | 值得注意的是,在Rust中赋值 (如`x = 5`) 也是一个表达式,返回unit的值`()`。 30 | 31 | ## For 32 | 33 | Rust中的`for`循环与C语言的风格非常不同,抽象结构如下: 34 | 35 | ```rust 36 | for var in expression { 37 | code 38 | } 39 | ``` 40 | 41 | 其中`expression`是一个迭代器 (iterator),具体的例子为`0..10` (不包含最后一个值), 42 | 或者`[0, 1, 2].iter()`。 43 | 44 | ## While 45 | 46 | Rust中的`while`循环与C语言中的类似。对于无限循环,Rust有一个专用的关键字`loop`。 47 | 如果需要提前退出循环,可以使用关键字`break`或者`continue`, 48 | 还允许在循环的开头设定标签 (同样适用于`for`循环): 49 | 50 | ```rust 51 | 'outer: loop { 52 | println!("Entered the outer loop"); 53 | 54 | 'inner: loop { 55 | println!("Entered the inner loop"); 56 | break 'outer; 57 | } 58 | 59 | println!("This point will never be reached"); 60 | } 61 | 62 | println!("Exited the outer loop"); 63 | ``` 64 | 65 | ## Match 66 | 67 | Rust中的`match`表达式非常强大,首先看一个例子: 68 | 69 | ```rust 70 | let day = 5; 71 | 72 | match day { 73 | 0 | 6 => println!("weekend"), 74 | 1 ... 5 => println!("weekday"), 75 | _ => println!("invalid"), 76 | } 77 | ``` 78 | 79 | 其中`|`用于匹配多个值,`...`匹配一个范围 (包含最后一个值),并且`_`在这里是必须的, 80 | 因为`match`强制进行穷尽性检查 (exhaustiveness checking),必须覆盖所有的可能值。 81 | 如果需要得到`|`或者`...`匹配到的值,可以使用`@`绑定变量: 82 | 83 | ```rust 84 | let x = 1; 85 | 86 | match x { 87 | e @ 1 ... 5 => println!("got a range element {}", e), 88 | _ => println!("anything"), 89 | } 90 | ``` 91 | 92 | 使用`ref`关键字来得到一个引用: 93 | 94 | ```rust 95 | let x = 5; 96 | let mut y = 5; 97 | 98 | match x { 99 | // the `r` inside the match has the type `&i32` 100 | ref r => println!("Got a reference to {}", r), 101 | } 102 | 103 | match y { 104 | // the `mr` inside the match has the type `&i32` and is mutable 105 | ref mut mr => println!("Got a mutable reference to {}", mr), 106 | } 107 | ``` 108 | 109 | 再看一个使用`match`表达式来解构元组的例子: 110 | 111 | ```rust 112 | let pair = (0, -2); 113 | 114 | match pair { 115 | (0, y) => println!("x is `0` and `y` is `{:?}`", y), 116 | (x, 0) => println!("`x` is `{:?}` and y is `0`", x), 117 | _ => println!("It doesn't matter what they are"), 118 | } 119 | ``` 120 | 121 | `match`的这种解构同样适用于结构体或者枚举。如果有必要,还可以使用`..`来忽略域或者数据: 122 | 123 | ```rust 124 | struct Point { 125 | x: i32, 126 | y: i32, 127 | } 128 | 129 | let origin = Point { x: 0, y: 0 }; 130 | 131 | match origin { 132 | Point { x, .. } => println!("x is {}", x), 133 | } 134 | 135 | enum OptionalInt { 136 | Value(i32), 137 | Missing, 138 | } 139 | 140 | let x = OptionalInt::Value(5); 141 | 142 | match x { 143 | // 这里是 match 的 if guard 表达式,我们将在以后的章节进行详细介绍 144 | OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), 145 | OptionalInt::Value(..) => println!("Got an int!"), 146 | OptionalInt::Missing => println!("No such luck."), 147 | } 148 | ``` 149 | 150 | 此外,Rust还引入了`if let`和`while let`进行模式匹配: 151 | 152 | ```rust 153 | let number = Some(7); 154 | let mut optional = Some(0); 155 | 156 | // If `let` destructures `number` into `Some(i)`, evaluate the block. 157 | if let Some(i) = number { 158 | println!("Matched {:?}!", i); 159 | } else { 160 | println!("Didn't match a number!"); 161 | } 162 | 163 | // While `let` destructures `optional` into `Some(i)`, evaluate the block. 164 | while let Some(i) = optional { 165 | if i > 9 { 166 | println!("Greater than 9, quit!"); 167 | optional = None; 168 | } else { 169 | println!("`i` is `{:?}`. Try again.", i); 170 | optional = Some(i + 1); 171 | } 172 | } 173 | ``` 174 | 175 | -------------------------------------------------------------------------------- /collections/vec.md: -------------------------------------------------------------------------------- 1 | # 动态数组Vec 2 | 在第七章我们粗略介绍了一下Vec的用法。实际上,作为Rust中一个非常重要的数据类型,熟练掌握Vec的用法能大大提升我们在Rust世界中的编码能力。 3 | 4 | ## 特性及声明方式 5 | 6 | 和我们之前接触到的Array不同,`Vec`具有动态的添加和删除元素的能力,并且能够以`O(1)`的效率进行随机访问。同时,对其尾部进行push或者pop操作的效率也是平摊`O(1)`的。 7 | 同时,有一个非常重要的特性(虽然我们编程的时候大部分都不会考量它)就是,Vec的所有内容项都是生成在堆空间上的,也就是说,你可以轻易的将Vec move出一个栈而不用担心内存拷贝影响执行效率——毕竟只是拷贝的栈上的指针。 8 | 9 | 另外的就是,`Vec`中的泛型`T`必须是`Sized`的,也就是说必须在编译的时候就知道存一个内容项需要多少内存。对于那些在编译时候未知大小的项(函数类型等),我们可以用`Box`将其包裹,当成一个指针。 10 | 11 | ### new 12 | 我们可以用`std::vec::Vec::new()`的方式来声明一个Vec。 13 | 14 | ```rust 15 | let mut v1: Vec = Vec::new(); 16 | ``` 17 | 18 | 这里需要注意的是,`new`函数并没有提供一个能显式规定其泛型类型的参数,也就是说,上面的代码能根据`v1`的类型自动推导出`Vec`的泛型;但是,你不能写成如下的形式: 19 | 20 | ```rust 21 | let mut v1 = Vec::new::(); 22 | // 与之对比的,collect函数就能指定: 23 | // let mut v2 = (0i32..5).collect::>(); 24 | ``` 25 | 26 | 这是因为这两个函数的声明形式以及实现形式,在此,我们不做深究。 27 | 28 | 29 | ### 宏声明 30 | 31 | 相比调用new函数,Rust提供了一种更加直观便捷的方式声明一个动态数组: `vec!` 宏。 32 | 33 | ```rust 34 | let v: Vec = vec![]; 35 | 36 | // 以下语句相当于: 37 | // let mut temp = Vec::new(); 38 | // temp.push(1); 39 | // temp.push(2); 40 | // temp.push(3); 41 | // let v = temp; 42 | let v = vec![1, 2, 3]; 43 | 44 | let v = vec![0; 10]; //注意分号,这句话声明了一个 内容为10个0的动态数组 45 | ``` 46 | 47 | ### 从迭代器生成 48 | 49 | 因为Vec实现了`FromIterator`这个trait,因此,借助collect,我们能将任意一个迭代器转换为Vec。 50 | 51 | ```rust 52 | let v: Vec<_> = (1..5).collect(); 53 | ``` 54 | 55 | ## 访问及修改 56 | 57 | ### 随机访问 58 | 59 | 就像数组一样,因为Vec借助`Index`和`IndexMut`提供了随机访问的能力,我们通过`[index]`来对其进行访问,当然,既然存在随机访问就会出现越界的问题。而在Rust中,一旦越界的后果是极其严重的,可以导致Rust当前线程panic。因此,除非你确定自己在干什么或者在`for`循环中,不然我们不推荐通过下标访问。 60 | 61 | 以下是例子: 62 | 63 | ```rust 64 | let a = vec![1, 2, 3]; 65 | assert_eq!(a[1usize], 2); 66 | ``` 67 | 68 | 那么,Rust中有没有安全的下标访问机制呢?答案是当然有:—— `.get(n: usize)` (`.get_mut(n: usize)`) 函数。 69 | 对于一个数组,这个函数返回一个`Option<&T>` (`Option<&mut T>`),当Option==None的时候,即下标越界,其他情况下,我们能安全的获得一个Vec里面元素的引用。 70 | 71 | ```rust 72 | let v =vec![1, 2, 3]; 73 | assert_eq!(v.get(1), Some(&2)); 74 | assert_eq!(v.get(3), None); 75 | ``` 76 | 77 | ### 迭代器 78 | 79 | 对于一个可变数组,Rust提供了一种简单的遍历形式—— for 循环。 80 | 我们可以获得一个数组的引用、可变引用、所有权。 81 | 82 | ```rust 83 | let v = vec![1, 2, 3]; 84 | for i in &v { .. } // 获得引用 85 | for i in &mut v { .. } // 获得可变引用 86 | for i in v { .. } // 获得所有权,注意此时Vec的属主将会被转移!! 87 | ``` 88 | 89 | 但是,这么写很容易出现多层`for`循环嵌套,因此,`Vec`提供了一个`into_iter()`方法,能显式地将自己转换成一个迭代器。然而迭代器怎么用呢?我们下一章将会详细说明。 90 | 91 | ### push的效率研究 92 | 93 | 前面说到,`Vec`有两个`O(1)`的方法,分别是`pop`和`push`,它们分别代表着将数据从尾部弹出或者装入。理论上来说,因为`Vec`是支持随机访问的,因此`push`效率应该是一致的。但是实际上,因为Vec的内部存在着内存拷贝和销毁,因此,如果你想要将一个数组,从零个元素开始,一个一个的填充直到最后生成一个非常巨大的数组的话,预先为其分配内存是一个非常好的办法。 94 | 95 | 这其中,有个关键的方法是reserve。 96 | 97 | 如下代码(注意:由于SystemTime API在1.8以后才稳定, 请使用1.8.0 stable 以及以上版本的rustc编译): 98 | 99 | ```rust 100 | use std::time; 101 | 102 | fn push_1m(v: &mut Vec, total: usize) { 103 | let e = time::SystemTime::now(); 104 | for i in 1..total { 105 | v.push(i); 106 | } 107 | let ed = time::SystemTime::now(); 108 | println!("time spend: {:?}", ed.duration_since(e).unwrap()); 109 | } 110 | 111 | fn main() { 112 | let mut v: Vec = vec![]; 113 | push_1m(&mut v, 5_000_000); 114 | let mut v: Vec = vec![]; 115 | v.reserve(5_000_000); 116 | push_1m(&mut v, 5_000_000); 117 | } 118 | ``` 119 | 120 | 在笔者自己的笔记本上,编译好了debug的版本,上面的代码跑出了: 121 | 122 | ``` 123 | ➜ debug git:(master) ✗ time ./demo 124 | time spend: Duration { secs: 0, nanos: 368875346 } 125 | time spend: Duration { secs: 0, nanos: 259878787 } 126 | ./demo 0.62s user 0.01s system 99% cpu 0.632 total 127 | 128 | ``` 129 | 130 | 好像并没有太大差异?然而切换到release版本的时候: 131 | 132 | ``` 133 | ➜ release git:(master) ✗ time ./demo 134 | time spend: Duration { secs: 0, nanos: 53389934 } 135 | time spend: Duration { secs: 0, nanos: 24979520 } 136 | ./demo 0.06s user 0.02s system 97% cpu 0.082 total 137 | ``` 138 | 139 | 注意消耗的时间的位数。可见,在去除掉debug版本的调试信息之后,是否预分配内存消耗时间降低了一倍! 140 | 141 | 这样的成绩,可见,预先分配内存确实有助于提升效率。 142 | 143 | 有人可能会问了,你这样纠结这点时间,最后不也是节省在纳秒级别的么,有意义么?当然有意义。 144 | 145 | 第一,纳秒也是时间,这还是因为这个测试的`Vec`只是最简单的内存结构。一旦涉及到大对象的拷贝,所花费的时间可就不一定这么少了。 146 | 第二,频繁的申请和删除堆空间,其内存一旦达到瓶颈的时候你的程序将会异常危险。 147 | 148 | 更多`Vec`的操作,请参照标准库的api。 149 | -------------------------------------------------------------------------------- /flow/repetition.md: -------------------------------------------------------------------------------- 1 | # 循环 2 | 3 | - for 4 | - while 5 | - loop 6 | - break 与 continue 7 | - label 8 | 9 | 10 | ## for 11 | 12 | for 语句用于遍历一个迭代器。 13 | 14 | ```rust 15 | for var in iterator { 16 | code 17 | } 18 | ``` 19 | 20 | Rust 迭代器返回一系列的元素,每个元素是循环中的一次重复。然后它的值与 var 绑定,它在循环体中有效。每当循环体执行完后,我们从迭代器中取出下一个值,然后我们再重复一遍。当迭代器中不再有值时,for 循环结束。 21 | 22 | 比如: 23 | 24 | ```rust 25 | for x in 0..10 { 26 | println!("{}", x); // x: i32 27 | } 28 | ``` 29 | 30 | 输出 31 | 32 | ``` 33 | 0 34 | 1 35 | 2 36 | 3 37 | 4 38 | 5 39 | 6 40 | 7 41 | 8 42 | 9 43 | ``` 44 | 45 | 不熟悉迭代器概念的同学可能傻眼了,下面不妨用 C 形式的 for 语句做下对比: 46 | 47 | ```rust 48 | // C 语言的 for 循环例子 49 | for (x = 0; x < 10; x++) { 50 | printf( "%d\n", x ); 51 | } 52 | ``` 53 | 54 | 两者输出是相同的,那么,为何 Rust 要这样来设计 for 语句呢? 55 | 56 | 1. 简化边界条件的确定,减少出错; 57 | 2. 减少运行时边界检查,提高性能。 58 | 59 | 即使对于有经验的 C 语言开发者来说,要手动控制要循环的每个元素也都是复杂并且易于出错的。 60 | 61 | for 语句就是迭代器遍历的语法糖。 62 | 63 | 上述迭代器的形式虽好,但是好像在循环过程中,少了索引信息。Rust 考虑到了这一点,当你需要记录你已经循环了多少次了的时候,你可以使用 `.enumerate()` 函数。比如: 64 | 65 | ```rust 66 | for (i,j) in (5..10).enumerate() { 67 | println!("i = {} and j = {}", i, j); 68 | } 69 | ``` 70 | 71 | 输出: 72 | 73 | ``` 74 | i = 0 and j = 5 75 | i = 1 and j = 6 76 | i = 2 and j = 7 77 | i = 3 and j = 8 78 | i = 4 and j = 9 79 | ``` 80 | 81 | 再比如: 82 | 83 | ```rust 84 | let lines = "Content of line one 85 | Content of line two 86 | Content of line three 87 | Content of line four".lines(); 88 | for (linenumber, line) in lines.enumerate() { 89 | println!("{}: {}", linenumber, line); 90 | } 91 | ``` 92 | 93 | 输出: 94 | 95 | ``` 96 | 0: Content of line one 97 | 1: Content of line two 98 | 2: Content of line three 99 | 3: Content of line four 100 | ``` 101 | 102 | 关于迭代器的知识,详见 **迭代器** 章节。 103 | 104 | ## while 105 | 106 | Rust 提供了 while 语句,条件表达式为真时,执行语句体。当你不确定应该循环多少次时可选择 while。 107 | 108 | ```rust 109 | while expression { 110 | code 111 | } 112 | ``` 113 | 114 | 比如: 115 | 116 | ```rust 117 | let mut x = 5; // mut x: i32 118 | let mut done = false; // mut done: bool 119 | 120 | while !done { 121 | x += x - 3; 122 | 123 | println!("{}", x); 124 | 125 | if x % 5 == 0 { 126 | done = true; 127 | } 128 | } 129 | ``` 130 | 131 | ## loop 132 | 133 | 有一种情况,我们经常会遇到,就是写一个无限循环: 134 | 135 | ```rust 136 | while true { 137 | // do something 138 | } 139 | ``` 140 | 141 | 针对这种情况,Rust 专门优化提供了一个语句 loop。 142 | 143 | ```rust 144 | loop { 145 | // do something 146 | } 147 | ``` 148 | 149 | `loop` 与 `while true` 的主要区别在编译阶段的静态分析。 150 | 151 | 比如说,如下代码: 152 | 153 | ```rust 154 | let mut a; 155 | loop { 156 | a = 1; 157 | // ... break ... 158 | } 159 | do_something(a) 160 | ``` 161 | 162 | 如果是`loop`循环,编译器会正确分析出变量`a`会被正确初始化,而如果换成`while true`,则会发生编译错误。这个微小的区别也会影响生命周期分析。 163 | 164 | ## break 和 continue 165 | 166 | 与 C 语言类似,Rust 也提供了 break 和 continue 两个关键字用来控制循环的流程。 167 | 168 | - break 用来跳出当前层的循环; 169 | - continue 用来执行当前层的下一次迭代。 170 | 171 | 像上面那个 while 例子: 172 | 173 | ```rust 174 | let mut x = 5; 175 | let mut done = false; 176 | 177 | while !done { 178 | x += x - 3; 179 | 180 | println!("{}", x); 181 | 182 | if x % 5 == 0 { 183 | done = true; 184 | } 185 | } 186 | ``` 187 | 188 | 可以优化成: 189 | 190 | ```rust 191 | let mut x = 5; 192 | 193 | loop { 194 | x += x - 3; 195 | 196 | println!("{}", x); 197 | 198 | if x % 5 == 0 { break; } 199 | } 200 | ``` 201 | 202 | 这样感觉更直观一点。 203 | 204 | 下面这个例子演示 continue 的用法: 205 | 206 | ```rust 207 | for x in 0..10 { 208 | if x % 2 == 0 { continue; } 209 | 210 | println!("{}", x); 211 | } 212 | ``` 213 | 214 | 它的作用是打印出 `0~9` 的奇数。结果如下: 215 | 216 | ``` 217 | 1 218 | 3 219 | 5 220 | 7 221 | 9 222 | ``` 223 | 224 | ## label 225 | 226 | 你也许会遇到这样的情形,当你有嵌套的循环而希望指定你的哪一个 break 或 continue 该起作用。就像大多数语言,默认 break 或 continue 将会作用于当前层的循环。当你想要一个 break 或 continue 作用于一个外层循环,你可以使用标签来指定你的 break 或 continue 语句作用的循环。 227 | 228 | 如下代码只会在 x 和 y 都为奇数时打印他们: 229 | 230 | ```rust 231 | 'outer: for x in 0..10 { 232 | 'inner: for y in 0..10 { 233 | if x % 2 == 0 { continue 'outer; } // continues the loop over x 234 | if y % 2 == 0 { continue 'inner; } // continues the loop over y 235 | println!("x: {}, y: {}", x, y); 236 | } 237 | } 238 | ``` 239 | -------------------------------------------------------------------------------- /quickstart/module-attribute.md: -------------------------------------------------------------------------------- 1 | # 模块与属性 2 | 3 | Rust有两个与模块 (module) 系统相关的独特术语:`crate`和`module`, 4 | 其中包装箱 (crate) 与其它语言中的 libary 或者 package 作用一样。 5 | 每个包装箱都有一个隐藏的根模块,在根模块下可以定义一个子模块树, 6 | 其路径采用`::`作为分隔符。包装箱由条目 (item) 构成,多个条目通过模块组织在一起。 7 | 8 | ## 定义模块 9 | 10 | 使用`mod`关键字定义我们的模块: 11 | 12 | ```rust 13 | // in src/lib.rs 14 | 15 | mod chinese { 16 | mod greetings { 17 | 18 | } 19 | 20 | mod farewells { 21 | 22 | } 23 | } 24 | 25 | mod english { 26 | mod greetings { 27 | 28 | } 29 | 30 | mod farewells { 31 | 32 | } 33 | } 34 | ``` 35 | 定义了四个子模块`chinese::{greetings, farewells}`和`english::{greetings, farewells}`。 36 | 模块默认是私有的,可以使用`pub`关键字将其设置成公开,只有公开的条目才允许在模块外部访问。 37 | 38 | 实践中更好的组织方式是将一个包装箱分拆到多个文件: 39 | 40 | ```rust 41 | // in src/lib.rs 42 | 43 | pub mod chinese; 44 | 45 | pub mod english; 46 | ``` 47 | 这两句声明告诉Rust查看`src/chinese.rs`和`src/english.rs`, 48 | 或者`src/chinese/mod.rs`和`src/english/mod.rs`。 49 | 先添加一些函数: 50 | 51 | ```rust 52 | // in src/chinese/greetings.rs 53 | 54 | pub fn hello() -> String { 55 | "你好!".to_string() 56 | } 57 | ``` 58 | 59 | ```rust 60 | // in src/chinese/farewells.rs 61 | 62 | pub fn goodbye() -> String { 63 | "再见!".to_string() 64 | } 65 | ``` 66 | 67 | ```rust 68 | // in src/english/greetings.rs 69 | 70 | pub fn hello() -> String { 71 | "Hello!".to_string() 72 | } 73 | ``` 74 | 75 | ```rust 76 | // in src/english/farewells.rs 77 | 78 | pub fn goodbye() -> String { 79 | "Goodbye!".to_string() 80 | } 81 | ``` 82 | 函数默认也是私有的,为了后面的使用我们需要`pub`关键字使其成为公有。 83 | 84 | ## 导入 crate 85 | 86 | 为了使用我们前面创建的名为`phrases`的包装箱,需要先声明导入 87 | 88 | ```rust 89 | // in src/main.rs 90 | 91 | extern crate phrases; 92 | 93 | fn main() { 94 | println!("Hello in Chinese: {}", phrases::chinese::greetings::hello()); 95 | } 96 | ``` 97 | 98 | Rust还有一个`use`关键字,允许我们导入包装箱中的条目到当前的作用域内: 99 | 100 | ```rust 101 | // in src/main.rs 102 | 103 | extern crate phrases; 104 | 105 | use phrases::chinese::greetings; 106 | use phrases::chinese::farewells::goodbye; 107 | 108 | fn main() { 109 | println!("Hello in Chinese: {}", greetings::hello()); 110 | println!("Goodbye in Chinese: {}", goodbye()); 111 | } 112 | ``` 113 | 但是,我们不推荐直接导入函数,这样更容易导致命名空间冲突,只导入模块是更好的做法。 114 | 如果要导入来自同一模块的多个条目,可以使用大括号简写: 115 | 116 | ```rust 117 | use phrases::chinese::{greetings, farewells}; 118 | ``` 119 | 如果是导入全部,可以使用通配符`*`。重命名可以使用`as`关键字: 120 | 121 | ```rust 122 | use phrases::chinese::greetings as chinese_greetings; 123 | ``` 124 | 125 | 有时我们需要将外部包装箱里面的函数导入到另一个模块内, 126 | 这时可以使用`pub use`来提供扩展接口而不映射代码层级结构。 127 | 比如 128 | 129 | ```rust 130 | // in src/english/mod.rs 131 | 132 | pub use self::greetings::hello; 133 | pub use self::farewells::goodbye; 134 | 135 | mod greetings; 136 | 137 | mod farewells; 138 | ``` 139 | 其中`pub use`声明将函数带入了当前模块中, 140 | 使得我们现在有了`phrases::english::hello()`函数和`phrases::english::goodbye()`函数, 141 | 即使它们的定义位于`phrases::english::greetings::hello()` 142 | 和`phrases::english::farewells::goodbye()`中, 143 | 内部代码的组织结构不能反映我们的扩展接口。 144 | 145 | 默认情况下,`use`声明表示从根包装箱开始的绝对路径。 146 | 此外,我们可以使用`use self::`表示相对于当前模块的位置, 147 | `use super::`表示当前位置的上一级,以`::`为前缀的路径表示根包装箱路径。 148 | 149 | ```rust 150 | use foo::baz::foobaz; // foo is at the root of the crate 151 | 152 | mod foo { 153 | use foo::bar::foobar; // foo is at crate root 154 | use self::baz::foobaz; // self refers to module 'foo' 155 | 156 | pub mod bar { 157 | pub fn foobar() { } 158 | } 159 | 160 | pub mod baz { 161 | use super::bar::foobar; // super refers to module 'foo' 162 | pub fn foobaz() { } 163 | } 164 | } 165 | ``` 166 | 167 | ## 属性 168 | 169 | 在Rust中,属性 (attribute) 是应用于包装箱、模块或者条目的元数据 (metadata), 170 | 主要用于: 171 | 172 | * 实现条件编译 (conditional compilation) 173 | * 设置包装箱名字、版本以及类型 174 | * 取消可疑代码的警告 175 | * 设置编译器选项 176 | * 链接外部库 177 | * 标记测试函数 178 | 179 | 属性有两种用法:`#![crate_attribute]`应用于整个包装箱, 180 | 而`#[crate_attribute]`应用于紧邻的一个模块或者条目。 181 | 属性的参数也有三种不同的形式: 182 | 183 | * `#[attribute = "value"]` 184 | * `#[attribute(key = "value")]` 185 | * `#[attribute(value)]` 186 | 187 | 下面列举几个经常用到的属性: 188 | 189 | * `#[path="foo.rs"]`用于设置一个模块需要载入的文件路径。 190 | * `#[allow(dead_code)]`用于取消对死代码的默认lint检查。 191 | * `#[derive(PartialEq, Clone)]`用于自动推导`PartialEq`和`Clone`这两个特性的实现。 192 | 193 | -------------------------------------------------------------------------------- /match/pattern.md: -------------------------------------------------------------------------------- 1 | # 模式 2 | 模式,是Rust另一个强大的特性。它可以被用在`let`和`match`表达式里面。相信大家应该还记得我们在[复合类型](../type/compound-types.md)中提到的关于在let表达式中解构元组的例子,实际上这就是一个模式。 3 | 4 | ```rust 5 | let tup = (0u8, 1u8); 6 | let (x, y) = tup; 7 | ``` 8 | 9 | 而且我们需要知道的是,如果一个模式中出现了和当前作用域中已存在的同名的绑定,那么它会覆盖掉外部的绑定。比如: 10 | 11 | ```rust 12 | let x = 1; 13 | let c = 'c'; 14 | 15 | match c { 16 | x => println!("x: {} c: {}", x, c), 17 | } 18 | 19 | println!("x: {}", x); 20 | ``` 21 | 22 | 它的输出结果是: 23 | 24 | ``` 25 | x: c c: c 26 | x: 1 27 | ``` 28 | 29 | 在以上代码中,match作用域里的`x`这个绑定被覆盖成了`'c'`,而出了这个作用域,绑定`x`又恢复为`1`。这和变量绑定的行为是一致的。 30 | 31 | ## 更强大的解构 32 | 33 | 在上一节里,我们初步了解了模式匹配在解构`enum`时候的便利性,事实上,在Rust中模式可以被用来对任何复合类型进行解构——struct/tuple/enum。现在我们要讲述一个复杂点的例子,对`struct`进行解构。 34 | 35 | 首先,我们可以对一个结构体进行标准的解构: 36 | 37 | ```rust 38 | struct Point { 39 | x: i64, 40 | y: i64, 41 | } 42 | let point = Point { x: 0, y: 0 }; 43 | match point { 44 | Point { x, y } => println!("({},{})", x, y), 45 | } 46 | ``` 47 | 48 | 最终,我们拿到了`Point`内部的值。有人说了,那我想改个名字怎么办? 49 | 很简单,你可以使用 `:`来对一个struct的字段进行重命名,如下: 50 | 51 | ```rust 52 | struct Point { 53 | x: i64, 54 | y: i64, 55 | } 56 | let point = Point { x: 0, y: 0 }; 57 | match point { 58 | Point { x: x1, y: y1} => println!("({},{})", x1, y1), 59 | } 60 | ``` 61 | 62 | 另外,有的时候我们其实只对某些字段感兴趣,就可以用`..`来省略其他字段。 63 | 64 | ```rust 65 | struct Point { 66 | x: i64, 67 | y: i64, 68 | } 69 | 70 | let point = Point { x: 0, y: 0 }; 71 | 72 | match point { 73 | Point { y, .. } => println!("y is {}", y), 74 | } 75 | ``` 76 | 77 | ## 忽略和内存管理 78 | 79 | 总结一下,我们遇到了两种不同的模式忽略的情况——`_`和`..`。这里要注意,模式匹配中被忽略的字段是不会被`move`的,而且实现`Copy`的也会优先被Copy而不是被`move`。 80 | 81 | 说的有点拗口,上代码: 82 | 83 | ```rust 84 | let tuple: (u32, String) = (5, String::from("five")); 85 | 86 | let (x, s) = tuple; 87 | 88 | // 以下行将导致编译错误,因为String类型并未实现Copy, 所以tuple被整体move掉了。 89 | // println!("Tuple is: {:?}", tuple); 90 | 91 | let tuple = (5, String::from("five")); 92 | 93 | // 忽略String类型,而u32实现了Copy,则tuple不会被move 94 | let (x, _) = tuple; 95 | 96 | println!("Tuple is: {:?}", tuple); 97 | ``` 98 | 99 | ## 范围和多重匹配 100 | 101 | 模式匹配可以被用来匹配单种可能,当然也就能被用来匹配多种情况: 102 | 103 | ### 范围 104 | 105 | 在模式匹配中,当我想要匹配一个数字(字符)范围的时候,我们可以用`...`来表示: 106 | 107 | ```rust 108 | let x = 1; 109 | 110 | match x { 111 | 1 ... 10 => println!("一到十"), 112 | _ => println!("其它"), 113 | } 114 | 115 | let c = 'w'; 116 | 117 | match c { 118 | 'a' ... 'z' => println!("小写字母"), 119 | 'A' ... 'Z' => println!("大写字母"), 120 | _ => println!("其他字符"), 121 | } 122 | ``` 123 | 124 | ### 多重匹配 125 | 126 | 当我们只是单纯的想要匹配多种情况的时候,可以使用 `|` 来分隔多个匹配条件 127 | 128 | ```rust 129 | let x = 1; 130 | 131 | match x { 132 | 1 | 2 => println!("一或二"), 133 | _ => println!("其他"), 134 | } 135 | ``` 136 | 137 | ## ref 和 ref mut 138 | 139 | 前面我们了解到,当被模式匹配命中的时候,未实现`Copy`的类型会被默认的move掉,因此,原owner就不再持有其所有权。但是有些时候,我们只想要从中拿到一个变量的(可变)引用,而不想将其move出作用域,怎么做呢?答:用`ref`或者`ref mut`。 140 | 141 | ```rust 142 | let mut x = 5; 143 | 144 | match x { 145 | ref mut mr => println!("mut ref :{}", mr), 146 | } 147 | // 当然了……在let表达式里也能用 148 | let ref mut mrx = x; 149 | ``` 150 | 151 | 152 | ## 变量绑定 153 | 154 | 在模式匹配的过程内部,我们可以用`@`来绑定一个变量名,这在复杂的模式匹配中是再方便不过的,比如一个具名的范围匹配如下: 155 | 156 | ```rust 157 | let x = 1u32; 158 | match x { 159 | e @ 1 ... 5 | e @ 10 ... 15 => println!("get:{}", e), 160 | _ => (), 161 | } 162 | ``` 163 | 164 | 如代码所示,e绑定了x的值。 165 | 166 | 当然,变量绑定是一个极其有用的语法,下面是一个来自官方doc里的例子: 167 | 168 | ```rust 169 | #[derive(Debug)] 170 | struct Person { 171 | name: Option, 172 | } 173 | 174 | let name = "Steve".to_string(); 175 | let x: Option = Some(Person { name: Some(name) }); 176 | match x { 177 | Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a), 178 | _ => {} 179 | } 180 | ``` 181 | 182 | 输出: 183 | 184 | ``` 185 | Some("Steve") 186 | ``` 187 | 188 | ## 后置条件 189 | 190 | 一个后置的if表达式可以被放在match的模式之后,被称为`match guards`。例如如下代码: 191 | 192 | ```rust 193 | let x = 4; 194 | let y = false; 195 | 196 | match x { 197 | 4 | 5 if y => println!("yes"), 198 | _ => println!("no"), 199 | } 200 | ``` 201 | 202 | 猜一下上面代码的输出? 203 | 204 | 答案是`no`。因为guard是后置条件,是整个匹配的后置条件:所以上面的式子表达的逻辑实际上是: 205 | 206 | ``` 207 | // 伪代码表示 208 | IF y AND (x IN List[4, 5]) 209 | ``` 210 | -------------------------------------------------------------------------------- /heap-stack/heap-stack.md: -------------------------------------------------------------------------------- 1 | # Heap & Stack 2 | 3 | ## 简介 4 | 5 | 堆和栈是计算机里面最基本的概念,不过如果一直使用高级语言如 Python/Ruby/PHP/Java 等之类的语言的话,可能对堆和栈并不怎么理解,当然这里的栈(Stack)并不是数据结构里面的概念,而是计算机对内存的一个抽象。相比而言,C/C++/Rust 这些语言就必须对堆和栈的概念非常了解才能写出正确的程序,之所以有这样的区别是因为它们的内存管理方式不同,Python 之类的语言程序运行时会同时会运行垃圾回收器,垃圾回收器与用户程序或并行执行或交错执行,垃圾回收器会自动释放不再使用的内存空间,而 C/C++/Rust 则没有垃圾回收器。 6 | 7 | 操作系统会将物理内存映射成虚拟地址空间,程序在启动时看到的虚拟地址空间是一块完整连续的内存。 8 | 9 | 栈内存从高位地址向下增长,且栈内存分配是连续的,一般操作系统对栈内存大小是有限制的,Linux/Unix 类系统上面可以通过 ulimit 设置最大栈空间大小,所以 C 语言中无法创建任意长度的数组。在Rust里,函数调用时会创建一个临时栈空间,调用结束后 Rust 会让这个栈空间里的对象自动进入 `Drop` 流程,最后栈顶指针自动移动到上一个调用栈顶,无需程序员手动干预,因而栈内存申请和释放是非常高效的。 10 | 11 | 相对地,堆上内存则是从低位地址向上增长,堆内存通常只受物理内存限制,而且通常是不连续的,一般由程序员手动申请和释放的,如果想申请一块连续内存,则操作系统需要在堆中查找一块未使用的满足大小的连续内存空间,故其效率比栈要低很多,尤其是堆上如果有大量不连续内存时。另外内存使用完也必须由程序员手动释放,不然就会出现内存泄漏,内存泄漏对需要长时间运行的程序(例如守护进程)影响非常大。 12 | 13 | ## Rust 中的堆和栈 14 | 15 | 由于函数栈在函数执行完后会销毁,所以栈上存储的变量不能在函数之间传递,这也意味着函数没法返回栈上变量的引用,而这通常是 C/C++ 新手常犯的错误。而 Rust 中编译器则会检查出这种错误,错误提示一般为 `xxx does not live long enough`,看下面一个例子 16 | 17 | 18 | ```rust 19 | fn main() { 20 | let b = foo("world"); 21 | println!("{}", b); 22 | } 23 | 24 | fn foo(x: &str) -> &str { 25 | let a = "Hello, ".to_string() + x; 26 | &a 27 | } 28 | ``` 29 | 30 | 之所以这样写,很多人觉得可以直接拷贝字符串 `a` 的引用从而避免拷贝整个字符串,然而得到的结果却是 `a does not live long enough` 的编译错误。因为引用了一个函数栈中临时创建的变量,函数栈在函数调用结束后会销毁,这样返回的引用就变得毫无意义了,指向了一个并不存在的变量。相对于 C/C++ 而言,使用 Rust 就会幸运很多,因为 C/C++ 中写出上面那样的程序,编译器会默默地让你通过直到运行时才会给你报错。 31 | 32 | 其实由于 `a` 本身是 String 类型,是使用堆来存储的,所以可以直接返回,在函数返回时函数栈销毁后依然存在。同时 Rust 中下面的代码实际上也只是浅拷贝。 33 | 34 | ```rust 35 | fn main() { 36 | let b = foo("world"); 37 | println!("{}", b); 38 | } 39 | 40 | fn foo(x: &str) -> String { 41 | let a = "Hello, ".to_string() + x; 42 | a 43 | } 44 | ``` 45 | 46 | Rust 默认使用栈来存储变量,而栈上内存分配是连续的,所以必须在编译之前了解变量占用的内存空间大小,编译器才能合理安排内存布局。 47 | 48 | ## Box 49 | 50 | C 里面是通过 malloc/free 手动管理堆上内存空间的,而 Rust 则有多种方式,其中最常用的一种就是 Box,通过 `Box::new()` 可以在堆上申请一块内存空间,不像 C 里面一样堆上空间需要手动调用 `free` 释放,Rust 中是在编译期编译器借助 lifetime 对堆内存生命期进行分析,在生命期结束时自动插入 `free`。当前 Rust 底层即 Box 背后是调用 jemalloc 来做内存管理的,所以堆上空间是不需要程序员手动去管理释放的。很多时候你被编译器虐得死去活来时,那些 `borrow`, `move`, `lifetime` 错误其实就是编译器在教你认识内存布局,教你用 lifetime 规则去控制内存。这套规则说难不难,说简单也不简单,以前用别的语言写程序时对内存不关心的,刚写起来可能真的会被虐得死去活来,但是一旦熟悉这套规则,对内存布局掌握清楚后,借助编译器的提示写起程序来就会如鱼得水,这套规则是理论界研究的成果在Rust编译器上的实践。 51 | 52 | 大多数带 GC 的面向对象语言里面的对象都是借助 box 来实现的,比如常见的动态语言 Python/Ruby/JavaScript 等,其宣称的"一切皆对象(Everything is an object)",里面所谓的对象基本上都是 boxed value。 53 | 54 | boxed 值相对于 unboxed,内存占用空间会大些,同时访问值的时候也需要先进行 unbox,即对指针进行解引用再获取真正存储的值,所以内存访问开销也会大些。既然 boxed 值既费空间又费时间,为什么还要这么做呢?因为通过 box,所有对象看起来就像是以相同大小存储的,因为只需要存储一个指针就够了,应用程序可以同等看待各种值,而不用去管实际存储是多大的值,如何申请和释放相应资源。 55 | 56 | Box 是堆上分配的内存,通过 `Box::new()` 会创建一个堆空间并返回一个指向堆空间的指针 57 | 58 | nightly 版本中引入 `box` 关键词,可以用来取代 `Box::new()` 申请一个堆空间,也可以用在模式匹配上面 59 | 60 | ```rust 61 | #![feature(box_syntax, box_patterns)] 62 | fn main() { 63 | let boxed = Some(box 5); 64 | match boxed { 65 | Some(box unboxed) => println!("Some {}", unboxed), 66 | None => println!("None"), 67 | } 68 | } 69 | ``` 70 | 71 | 下面看一个例子,对比一下 `Vec` 和 `Vec>` 内存布局,这两个图来自 [Stack Overflow](http://stackoverflow.com/questions/21066133/what-is-the-difference-between-veci32-and-vecboxi32/21067103#21067103),从这两张内存分布图可以清楚直观地看出 Box 是如何存储的 72 | 73 | 74 | ``` 75 | Vec 76 | 77 | (stack) (heap) 78 | ┌──────┐ ┌───┐ 79 | │ vec1 │──→│ 1 │ 80 | └──────┘ ├───┤ 81 | │ 2 │ 82 | ├───┤ 83 | │ 3 │ 84 | ├───┤ 85 | │ 4 │ 86 | └───┘ 87 | ``` 88 | 89 | 90 | ``` 91 | Vec> 92 | 93 | (stack) (heap) ┌───┐ 94 | ┌──────┐ ┌───┐ ┌─→│ 1 │ 95 | │ vec2 │──→│ │─┘ └───┘ 96 | └──────┘ ├───┤ ┌───┐ 97 | │ │───→│ 2 │ 98 | ├───┤ └───┘ 99 | │ │─┐ ┌───┐ 100 | ├───┤ └─→│ 3 │ 101 | │ │─┐ └───┘ 102 | └───┘ │ ┌───┐ 103 | └─→│ 4 │ 104 | └───┘ 105 | ``` 106 | 107 | 一些语言里会有看起来既像数组又像列表的数据结构,例如 python 中的 List,其实就是与 `Vec>` 类似,只是把 i32 换成任意类型,就操作效率而言比单纯的 List 高效,同时又比数组使用更灵活。 108 | 109 | 一般而言,在编译期间不能确定大小的数据类型都需要使用堆上内存,因为编译器无法在栈上分配 编译期未知大小 的内存,所以诸如 String, Vec 这些类型的内存其实是被分配在堆上的。换句话说,我们可以很轻松的将一个 Vec move 出作用域而不必担心消耗,因为数据实际上不会被复制。 110 | 111 | 另外,需要从函数中返回一个浅拷贝的变量时也需要使用堆内存而不能直接返回一个指向函数内部定义变量的引用。 112 | -------------------------------------------------------------------------------- /data-structure/priority_queue.md: -------------------------------------------------------------------------------- 1 | # 优先队列 2 | 3 | ## 简介 4 | 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (largest-in,first-out)的行为特征。 5 | 6 | >优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有: 7 | 8 | 1. 查找; 9 | 2. 插入一个新元素; 10 | 3. 删除。 11 | 12 | 在最小优先队列(min priority queue)中,查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素。优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行。 13 | 14 | ## 优先队列的实现: 15 | 16 | 首先定义 PriorityQueue 结构体 17 | 18 | ```rust 19 | #[derive(Debug)] 20 | struct PriorityQueue where T: PartialOrd + Clone { 21 | pq: Vec 22 | } 23 | ``` 24 | 25 | 第二行的`where T: PartialOrd + Clone`指的是 PriorityQueue 存储的泛型 T 是满足 `PartialOrd` 和 `Clone` trait 约束的,意味着泛型 T 是可排序和克隆的。 26 | 27 | 后面是一些基本的方法实现,比较简单,就直接看代码吧。这个优先队列是基于Vec实现的,有O(1)的插入和O(n)的最大/最小值出列。 28 | 29 | ```rust 30 | impl PriorityQueue where T: PartialOrd + Clone { 31 | fn new() -> PriorityQueue { 32 | PriorityQueue { pq: Vec::new() } 33 | } 34 | 35 | fn len(&self) -> usize { 36 | self.pq.len() 37 | } 38 | 39 | fn is_empty(&self) -> bool { 40 | self.pq.len() == 0 41 | } 42 | 43 | fn insert(&mut self, value: T) { 44 | self.pq.push(value); 45 | } 46 | 47 | fn max(&self) -> Option { 48 | if self.is_empty() { return None } 49 | let max = self.max_index(); 50 | Some(self.pq[max].clone()) 51 | } 52 | 53 | fn min(&self) -> Option { 54 | if self.is_empty() { return None } 55 | let min = self.min_index(); 56 | Some(self.pq[min].clone()) 57 | } 58 | 59 | fn delete_max(&mut self) -> Option { 60 | if self.is_empty() { return None; } 61 | let max = self.max_index(); 62 | Some(self.pq.remove(max).clone()) 63 | } 64 | 65 | fn delete_min(&mut self) -> Option { 66 | if self.is_empty() { return None; } 67 | let min = self.min_index(); 68 | Some(self.pq.remove(min).clone()) 69 | } 70 | 71 | fn max_index(&self) -> usize { 72 | let mut max = 0; 73 | for i in 1..self.pq.len() - 1 { 74 | if self.pq[max] < self.pq[i] { 75 | max = i; 76 | } 77 | } 78 | max 79 | } 80 | 81 | fn min_index(&self) -> usize { 82 | let mut min = 0; 83 | for i in 0..self.pq.len() - 1 { 84 | if self.pq[i] < self.pq[i + 1] { 85 | min = i; 86 | } 87 | } 88 | min 89 | } 90 | } 91 | ``` 92 | 93 | ## 测试代码: 94 | 95 | ```rust 96 | fn test_keep_min() { 97 | let mut pq = PriorityQueue::new(); 98 | pq.insert(3); 99 | pq.insert(2); 100 | pq.insert(1); 101 | pq.insert(4); 102 | assert!(pq.min().unwrap() == 1); 103 | } 104 | 105 | fn test_keep_max() { 106 | let mut pq = PriorityQueue::new(); 107 | pq.insert(2); 108 | pq.insert(4); 109 | pq.insert(1); 110 | pq.insert(3); 111 | assert!(pq.max().unwrap() == 4); 112 | } 113 | 114 | fn test_is_empty() { 115 | let mut pq = PriorityQueue::new(); 116 | assert!(pq.is_empty()); 117 | pq.insert(1); 118 | assert!(!pq.is_empty()); 119 | } 120 | 121 | fn test_len() { 122 | let mut pq = PriorityQueue::new(); 123 | assert!(pq.len() == 0); 124 | pq.insert(2); 125 | pq.insert(4); 126 | pq.insert(1); 127 | assert!(pq.len() == 3); 128 | } 129 | 130 | fn test_delete_min() { 131 | let mut pq = PriorityQueue::new(); 132 | pq.insert(3); 133 | pq.insert(2); 134 | pq.insert(1); 135 | pq.insert(4); 136 | assert!(pq.len() == 4); 137 | assert!(pq.delete_min().unwrap() == 1); 138 | assert!(pq.len() == 3); 139 | } 140 | 141 | fn test_delete_max() { 142 | let mut pq = PriorityQueue::new(); 143 | pq.insert(2); 144 | pq.insert(10); 145 | pq.insert(1); 146 | pq.insert(6); 147 | pq.insert(3); 148 | assert!(pq.len() == 5); 149 | assert!(pq.delete_max().unwrap() == 10); 150 | assert!(pq.len() == 4); 151 | } 152 | 153 | fn main() { 154 | test_len(); 155 | test_delete_max(); 156 | test_delete_min(); 157 | test_is_empty(); 158 | test_keep_max(); 159 | test_keep_min(); 160 | } 161 | ``` 162 | 163 | ## 练习 164 | 基于二叉堆实现一个优先队列,以达到O(1)的出列和O(log n)的入列 165 | -------------------------------------------------------------------------------- /data-structure/binary_tree.md: -------------------------------------------------------------------------------- 1 | # 二叉树 2 | 3 | ## 二叉树简介 4 | 在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。 5 | 6 | >二叉查找树的子节点与父节点的键一般满足一定的顺序关系,习惯上,左节点的键少于父亲节点的键,右节点的键大于父亲节点的键。 7 | 8 | >二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键总是大于或等于任何一个子节点的键;最小堆:父结点的键总是小于或等于任何一个子节点的键。 9 | 10 | >二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。 11 | 12 | >一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。 13 | 14 | ## 二叉树与树的区别 15 | 二叉树*不是*树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别: 16 | 17 | 1. 树中结点的最大度数没有限制,而二叉树结点的最大度数为2。 18 | 2. 树的结点无左、右之分,而二叉树的结点有左、右之分。 19 | 20 | ## 定义二叉树的结构 21 | 二叉树的每个节点由键key、值value与左右子树left/right组成,这里我们把节点声明为一个泛型结构。 22 | 23 | ```rust 24 | type TreeNode = Option>>; 25 | #[derive(Debug)] 26 | struct Node { 27 | left: TreeNode, 28 | right: TreeNode, 29 | key: K, 30 | value: V, 31 | } 32 | ``` 33 | 34 | ## 实现二叉树的初始化与二叉查找树的插入 35 | 由于二叉查找树要求键可排序,我们要求K实现PartialOrd 36 | 37 | ```rust 38 | trait BinaryTree { 39 | fn pre_order(&self); 40 | fn in_order(&self); 41 | fn pos_order(&self); 42 | } 43 | trait BinarySearchTree:BinaryTree { 44 | fn insert(&mut self, key:K,value: V); 45 | } 46 | impl Node { 47 | fn new(key: K,value: V) -> Self { 48 | Node{ 49 | left: None, 50 | right: None, 51 | value: value, 52 | key: key, 53 | } 54 | } 55 | } 56 | impl BinarySearchTree for Node{ 57 | fn insert(&mut self, key:K,value:V) { 58 | if self.key < key { 59 | if let Some(ref mut right) = self.right { 60 | right.insert(key,value); 61 | } else { 62 | self.right = Some(Box::new(Node::new(key,value))); 63 | } 64 | } else { 65 | if let Some(ref mut left) = self.left { 66 | left.insert(key,value); 67 | } else { 68 | self.left = Some(Box::new(Node::new(key,value))); 69 | } 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | ## 二叉树的遍历 76 | 77 | - 先序遍历:首先访问根,再先序遍历左(右)子树,最后先序遍历右(左)子树。 78 | - 中序遍历:首先中序遍历左(右)子树,再访问根,最后中序遍历右(左)子树。 79 | - 后序遍历:首先后序遍历左(右)子树,再后序遍历右(左)子树,最后访问根。 80 | 81 | 下面是代码实现: 82 | 83 | ```rust 84 | impl BinaryTree for Node { 85 | fn pre_order(&self) { 86 | println!("{}", self.value); 87 | 88 | if let Some(ref left) = self.left { 89 | left.pre_order(); 90 | } 91 | if let Some(ref right) = self.right { 92 | right.pre_order(); 93 | } 94 | } 95 | 96 | fn in_order(&self) { 97 | if let Some(ref left) = self.left { 98 | left.in_order(); 99 | } 100 | println!("{}", self.value); 101 | if let Some(ref right) = self.right { 102 | right.in_order(); 103 | } 104 | } 105 | fn pos_order(&self) { 106 | if let Some(ref left) = self.left { 107 | left.pos_order(); 108 | } 109 | if let Some(ref right) = self.right { 110 | right.pos_order(); 111 | } 112 | println!("{}", self.value); 113 | } 114 | } 115 | ``` 116 | 117 | ## 测试代码 118 | 119 | ```rust 120 | type BST = Node; 121 | 122 | fn test_insert() { 123 | let mut root = BST::::new(3,4); 124 | root.insert(2,3); 125 | root.insert(4,6); 126 | root.insert(5,5); 127 | root.insert(6,6); 128 | root.insert(1,8); 129 | if let Some(ref left) = root.left { 130 | assert_eq!(left.value, 3); 131 | } 132 | 133 | if let Some(ref right) = root.right { 134 | assert_eq!(right.value, 6); 135 | if let Some(ref right) = right.right { 136 | assert_eq!(right.value, 5); 137 | } 138 | } 139 | println!("Pre Order traversal"); 140 | root.pre_order(); 141 | println!("In Order traversal"); 142 | root.in_order(); 143 | println!("Pos Order traversal"); 144 | root.pos_order(); 145 | } 146 | 147 | fn main() { 148 | test_insert(); 149 | } 150 | ``` 151 | 152 | ## 练习 153 | 基于以上代码,修改成二叉堆的形式。 154 | -------------------------------------------------------------------------------- /type/types.md: -------------------------------------------------------------------------------- 1 | # 原生类型 2 | 3 | 像其他现代编程语言一样,Rust提供了一系列基础的类型,我们一般称之为*原生类型*。其强大的类型系统就是建立在这些原生类型之上的,因此,在写Rust代码之前,必须要对Rust的原生类型有一定的了解。 4 | 5 | ## bool 6 | 7 | Rust自带了`bool`类型,其可能值为`true`或者`false`。 8 | 我们可以通过这样的方式去声明它: 9 | 10 | ```rust 11 | let is_she_love_me = false; 12 | let mut is_he_love_me: bool = true; 13 | ``` 14 | 15 | 当然,bool类型被用的最多的地方就是在`if表达式`里了。 16 | 17 | ## char 18 | 19 | 在Rust中,一个`char`类型表示一个*Unicode*字符,这也就意味着,在某些语言里代表一个字符(8bit)的char,在Rust里实际上是四个字节(32bit)。 20 | 同时,我们可以将各种奇怪的非中文字符随心所欲的赋值给一个char类型。需要注意的是,Rust中我们要用`'`来表示一个char,如果用`"`的话你得到的实际上是一个`&'static str`。 21 | 22 | ```rust 23 | let c = 'x'; 24 | let cc = '王'; 25 | ``` 26 | 27 | ## 数字类型 28 | 29 | 和其他类C系的语言不一样,Rust用一种*符号+位数*的方式来表示其基本的数字类型。可能你习惯了`int`、`double`、`float`之类的表示法,Rust的表示法需要你稍微适应一下。 30 | 31 | 你可用的符号有 `i`、`f`、`u` 32 | 33 | 你可用的位数,当然了,都是2的n次幂,分别为`8`、`16`、`32`、`64`及`size`。 34 | 35 | 你可以将其组合起来,形成诸如`i32`,`u16`等类型。 36 | 37 | 当然了,这样的组合并不自由,因为浮点类型最少只能用32位来表示,因此只能有`f32`和`f64`来表示。 38 | 39 | ### 自适应类型 40 | 41 | 看完上面你一定会对`isize`和`usize`很好奇。这两个是来干啥的。这两个嘛,其实是取决于你的操作系统的位数。简单粗暴一点比如64位电脑上就是64位,32位电脑上就是32位,16位……呵呵哒。 42 | 43 | 但是需要注意的是,你不能因为你的电脑是64位的,而强行将它等同于64,也就是说`isize != i64`,任何情况下你都需要强制转换。 44 | 45 | ## 数组 array 46 | 47 | Rust的数组是被表示为`[T;N]`。其中N表示数组大小,并且这个大小一定是个编译时就能获得的整数值,T表示`泛型`类型,即任意类型。我们可以这么来声明和使用一个数组: 48 | 49 | ```rust 50 | let a = [8, 9, 10]; 51 | let b: [u8;3] = [8, 6, 5]; 52 | print!("{}", a[0]); 53 | ``` 54 | 55 | 和Golang一样,Rust的数组中的`N`(大小)也是类型的一部分,即`[u8; 3] != [u8; 4]`。这么设计是为了更安全和高效的使用内存,当然了,这会给第一次接触类似概念的人带来一点点困难,比如以下代码。 56 | 57 | ```rust 58 | fn show(arr: [u8;3]) { 59 | for i in &arr { 60 | print!("{} ", i); 61 | } 62 | } 63 | 64 | fn main() { 65 | let a: [u8; 3] = [1, 2, 3]; 66 | show(a); 67 | let b: [u8; 4] = [1, 2, 3, 4]; 68 | show(b); 69 | } 70 | ``` 71 | 72 | 编译运行它你将获得一个编译错误: 73 | 74 | ``` 75 | :11:10: 11:11 error: mismatched types: 76 | expected `[u8; 3]`, 77 | found `[u8; 4]` 78 | (expected an array with a fixed size of 3 elements, 79 | found one with 4 elements) [E0308] 80 | :11 show(b); 81 | ^ 82 | :11:10: 11:11 help: see the detailed explanation for E0308 83 | error: aborting due to previous error 84 | ``` 85 | 86 | 这是因为你将一个4长度的数组赋值给了一个只需要3长度数组作为参数的函数。那么如何写一个通用的show方法来展现任意长度数组呢?请看下节`Slice` 87 | 88 | ## Slice 89 | 90 | `Slice`从直观上讲,是对一个`Array`的切片,通过`Slice`,你能获取到一个`Array`的部分或者全部的访问权限。和`Array`不同,`Slice`是可以动态的,但是呢,其范围是不能超过`Array`的大小,这点和Golang是不一样的。 91 | 92 | 一个`Slice`的表达式可以为如下: `&[T]` 或者 `&mut [T]`。 93 | 94 | 这里`&`符号是一个难点,我们不妨放开这个符号,简单的把它看成是`Slice`的甲鱼臀部——规定。另外,同样的,`Slice`也是可以通过下标的方式访问其元素,下标也是从0开始的哟。 95 | 你可以这么声明并使用一个`Slice`: 96 | 97 | ```rust 98 | let arr = [1, 2, 3, 4, 5, 6]; 99 | let slice_complete = &arr[..]; // 获取全部元素 100 | let slice_middle = &arr[1..4]; // 获取中间元素,最后取得的Slice为 [2, 3, 4] 。切片遵循左闭右开原则。 101 | let slice_right = &arr[1..]; // 最后获得的元素为[2, 3, 4, 5, 6],长度为5。 102 | let slice_left = &arr[..3]; // 最后获得的元素为[1, 2, 3],长度为3。 103 | ``` 104 | 105 | 怎么样,了解了吧。 106 | 那么接下来我们用`Slice`来改造一下上面的函数 107 | 108 | ```rust 109 | fn show(arr: &[u8]) { 110 | for i in arr { 111 | print!("{} ", i); 112 | } 113 | println!(""); 114 | } 115 | 116 | fn main() { 117 | let a: [u8; 3] = [1, 2, 3]; 118 | let slice_a = &a[..]; 119 | show(slice_a); 120 | let b: [u8; 4] = [1, 2, 3, 4]; 121 | show(&b[..]); 122 | } 123 | ``` 124 | 输出 125 | ``` 126 | 1 2 3 127 | 1 2 3 4 128 | ``` 129 | 130 | ## 动态数组 Vec 131 | 132 | 熟悉C++ STL的同学可能对C++的vector很熟悉,同样的,Rust也提供了一个类似的东西。他叫`Vec`。 133 | 134 | 在基础类型里讲`Vec`貌似是不太合适的,但在实际应用中的应用比较广泛,所以说先粗略的介绍一下,在集合类型的章节会有详细讲述。 135 | 136 | 在Rust里,`Vec`被表示为 `Vec`, 其中T是一个泛型。 137 | 138 | 下面介绍几种典型的`Vec`的用法: 139 | 140 | ```rust 141 | let mut v1: Vec = vec![1, 2, 3]; // 通过vec!宏来声明 142 | let v2 = vec![0; 10]; // 声明一个初始长度为10的值全为0的动态数组 143 | println!("{}", v1[0]); // 通过下标来访问数组元素 144 | 145 | for i in &v1 { 146 | print!("{}", i); // &Vec 可以通过 Deref 转换成 &[i32] 147 | } 148 | 149 | println!(""); 150 | 151 | for i in &mut v1 { 152 | *i = *i+1; 153 | print!("{}", i); // 可变访问 154 | } 155 | 156 | ``` 157 | 158 | 输出结果: 159 | 160 | ``` 161 | 1 162 | 123 163 | 234 164 | ``` 165 | 166 | ## 最原生字符串 str 167 | 168 | 你可以用`str`来声明一个字符串,事实上,Rust中,所有用`""`包裹起来的都可以称为`&str`(注意这个`&`,这是难点,不用管他,不是么?),但是这个类型被单独用的情况很少,因此,我们将在下一节着重介绍字符串类型。 169 | 170 | ## 函数类型 Functions 171 | 172 | 函数同样的是一个类型,这里只给大家普及一些基本的概念,函数类型涉及到比较高阶的应用,希望大家能在后面的`闭包`章节仔细参读 173 | 174 | 下面是一个小例子 175 | 176 | ```rust 177 | fn foo(x: i32) -> i32 { x+1 } 178 | 179 | let x: fn(i32) -> i32 = foo; 180 | 181 | assert_eq!(11, x(10)); 182 | ``` 183 | -------------------------------------------------------------------------------- /editors/spacemacs.md: -------------------------------------------------------------------------------- 1 | # Spacemacs 2 | 3 | spacemacs,是一个给vimer的Emacs。 4 | ## 简介 5 | spacemacs是一个专门给那些习惯vim的操作,同时又向往emacs的扩展能力的人。它非常适合我这种折腾过vim,配置过emacs的人,但同时也欢迎任何没有基础的新人使用。简单来说,它是一个开箱即用的Emacs!这对一个比很多人年龄都大的软件来说是一件极其不容易的事情。 6 | 7 | ## 安装 8 | 由于笔者自己在linux平台,并没有windows平台的经验,所以在这里便不献丑了,期待各位补充。另外,windows平台真的需要么,斜眼瞅向了Visual Studio。 9 | 10 | ### Emacs安装 11 | 12 | 在*nix系统中,都不一定会默认安装了Emacs,就算安装了,也不一定是最新的版本。在这里,我强烈建议各位卸载掉系统自带的Emacs,因为你不知道系统给你安装的是个什么奇怪的存在,最遭心的,我碰见过只提供阉割版Emacs的linux发行版。 13 | 14 | 建议各位自己去emacs项目主页下载Emacs-24.5(本书写作时的最新版)极其以上版本,然后下载下来源码。至于Emacs的安装也非常简单,linux平台老三步。 15 | 16 | ```bash 17 | ./configure 18 | make 19 | sudo make install 20 | ``` 21 | 22 | 什么?你没有make?没有GCC?缺少依赖? 23 | 请安装它们…… 24 | 25 | ### Spacemacs安装 26 | 27 | 前面说了,Spacemacs就是个Emacs的配置文件库,因此我们可以通过非常简单的方式安装它: 28 | 29 | ```bash 30 | git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d 31 | mv ~/.emacs ~/_emacs.backup 32 | cd ~/.emacs.d 33 | echo $(git describe --tags $(git rev-list --tags --max-count=1)) | xargs git checkout 34 | ``` 35 | 36 | 其中,后三行是笔者加的,这里必须要吐槽一下的是,Spacemacs的master分支实际上是极其落后而且有错误的!其目前的release都是从develop分支上打的tag。 37 | 38 | 因此,一!定!不!要!用!主!分!支! 39 | 40 | 最后,之所以要加最后一行,这是笔者安装的时候的release的一个小bug,没有这个文件的话,emacs并不会顺利的完成初始化。 41 | 42 | 好了,配置文件我们已经搞定了,接下来,启动你的emacs,spacemacs会自动的去网上下载你需要的插件安装包。另外,能自备梯子的最好,因为你要下的东西不大,但是这个网络确实比较捉急。 43 | 44 | ### 前期准备 45 | 46 | 为了让Spacemacs支持Rust,我们还需要一点小小的配置。首先,请参照[前期准备](../editors/before.md),安装好你的racer。 47 | 48 | 在这里,强烈建议将racer的环境变量加入到系统变量中(通常他们在`/etc/profile/`里进行配置)并重新启动系统,因为真的有很多人直接点击emacs的图标启动它的,这样做很可能导致emacs并不继承自己的环境变量,这是很令人无奈的。 49 | 50 | ## 完成配置 51 | 52 | ### 修改标准的Spacemacs配置。 53 | 54 | Spacemacs文档中提供了一份标准的spacemacs[配置文件](https://github.com/syl20bnr/spacemacs/blob/master/core/templates/.spacemacs.template),你可以将其加入到你自己的`~/.spacemacs`文件中。 55 | 56 | 这里,我们需要修改的是其关于自定义插件的部分: 57 | 58 | ```lisp 59 | (defun dotspacemacs/layers () 60 | "Configuration Layers declaration. 61 | You should not put any user code in this function besides modifying the variable 62 | values." 63 | (setq-default 64 | ;; Base distribution to use. This is a layer contained in the directory 65 | ;; `+distribution'. For now available distributions are `spacemacs-base' 66 | ;; or `spacemacs'. (default 'spacemacs) 67 | dotspacemacs-distribution 'spacemacs 68 | ;; List of additional paths where to look for configuration layers. 69 | ;; Paths must have a trailing slash (i.e. `~/.mycontribs/') 70 | dotspacemacs-configuration-layer-path '() 71 | ;; List of configuration layers to load. If it is the symbol `all' instead 72 | ;; of a list then all discovered layers will be installed. 73 | dotspacemacs-configuration-layers 74 | '( 75 | ;; ---------------------------------------------------------------- 76 | ;; Example of useful layers you may want to use right away. 77 | ;; Uncomment some layer names and press (Vim style) or 78 | ;; (Emacs style) to install them. 79 | ;; ---------------------------------------------------------------- 80 | auto-completion 81 | better-defaults 82 | git 83 | spell-checking 84 | syntax-checking 85 | version-control 86 | rust 87 | ) 88 | ;; List of additional packages that will be installed without being 89 | ;; wrapped in a layer. If you need some configuration for these 90 | ;; packages then consider to create a layer, you can also put the 91 | ;; configuration in `dotspacemacs/config'. 92 | dotspacemacs-additional-packages '() 93 | ;; A list of packages and/or extensions that will not be install and loaded. 94 | dotspacemacs-excluded-packages '() 95 | ;; If non-nil spacemacs will delete any orphan packages, i.e. packages that 96 | ;; are declared in a layer which is not a member of 97 | ;; the list `dotspacemacs-configuration-layers'. (default t) 98 | dotspacemacs-delete-orphan-packages t)) 99 | 100 | ;; ... 101 | ;; 以下配置文件内容省略 102 | ;; ... 103 | ``` 104 | 105 | 注意`dotspacemacs-configuration-layers`的配置和标准配置文件的不同。 106 | 107 | 将配置文件保存,然后重启你的emacs,当然,我们也可以按`SPC f e R`来完成重载配置文件的目的,然后你会发现emacs会开始下一轮下载,稍等其完成。 108 | 109 | 在上一步中,我们已经完成了对Racer的环境变量的配置,所以,现在你的Spacemacs已经配置完成了!这种简便的配置形式,几乎能和Atom抗衡了。 110 | 111 | ### 按键绑定 112 | 如下,spacemacs默认提供了几种按键绑定,但是,笔者并不觉得这些很好用,还是更喜欢用命令行。 113 | 114 | | Key Binding | Description | 115 | |-------------|-----------------------------------| 116 | | ~SPC m c c~ | compile project with Cargo | 117 | | ~SPC m c t~ | run tests with Cargo | 118 | | ~SPC m c d~ | generate documentation with Cargo | 119 | | ~SPC m c x~ | execute the project with Cargo | 120 | 121 | ## 尝试 122 | 123 | 现在开始,我们可以打开一个Cargo项目,并且去使用它了。你会惊讶的发现racer/flycheck/company这三个插件配合在一起的时候是那么的和谐简单。 124 | -------------------------------------------------------------------------------- /data-structure/stack.md: -------------------------------------------------------------------------------- 1 | # 栈 2 | 3 | ## 栈简介 4 | 5 | - 栈作为一种数据结构,是一种只能在**一端**进行**插入**和**删除**操作的特殊线性表。 6 | 7 | - 它按照**先进后出**的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。 8 | 9 | >栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 10 | 11 | ## 栈的实现步骤: 12 | 13 | - 定义一个栈结构`Stack` 14 | - 定义组成栈结构的栈点`StackNode` 15 | - 实现栈的初始化函数`new( )` 16 | - 实现进栈函数`push( )` 17 | - 实现退栈函数`pop( )` 18 | 19 | ## 定义一个栈结构`Stack` 20 | 21 | ```rust 22 | #[derive(Debug)] 23 | struct Stack { 24 | top: Option>>, 25 | } 26 | ``` 27 | 28 | 让我们一步步来分析 29 | 30 | - 第一行的`#[derive(Debug)]`是为了让`Stack`结构体可以打印调试。 31 | - 第二行是定义了一个`Stack`结构体,这个结构体包含一个泛型参数`T`。 32 | - 第三行比较复杂,在定义`StackNode`的时候介绍 33 | 34 | ## 定义组成栈结构的栈点`StackNode` 35 | 36 | ```rust 37 | #[derive(Clone,Debug)] 38 | struct StackNode { 39 | val: T, 40 | next: Option>>, 41 | } 42 | ``` 43 | 44 | 在这段代码的第三行, 我们定义了一个`val`保存`StackNode`的值。 45 | 46 | >现在我们重点来看看第四行: 47 | 我们**从里到外**拆分来看看,首先是`Box`,这里的`Box`是 Rust 用来显式分配堆内存的类型: 48 | 49 | > `pub struct Box where T: ?Sized(_);` 50 | [详细文档请参考Rust的标准库](http://doc.rust-lang.org/nightly/std/boxed/struct.Box.html) 51 | 52 | > 在 Rust 里面用强大的类型系统做了统一的抽象。在这里相当于在堆空间里申请了一块内存保存`StackNode`。 53 | 54 | > **为什么要这么做了?如果不用Box封装会怎么样呢?** 55 | 56 | > 如果不用 Box 封装,rustc 编译器会报错,在 Rust 里面,rustc 默认使用栈空间,但是这里的`StackNode`定义的时候使用了递归的数据结构,next 属性的类型是 `StackNode`,而这个类型是无法确定大小的,所有这种无法确定大小的类型,都不能保存在栈空间。所以需要使用`Box`来封装。这样的话`next`的类型就是一个指向某一块堆空间的指针,而指针是可以确定大小的,因此能够保存在栈空间。 57 | 58 | > **那么为什么还需要使用`Option`来封装呢?** 59 | 60 | > `Option`是 Rust 里面的一个抽象类型,定义如下: 61 | > 62 | 63 | ```rust 64 | pub enum Option { 65 | None, 66 | Some(T), 67 | } 68 | ``` 69 | 70 | Option 里面包括元素,None 和 Some(T) ,这样就很轻松的描述了 next 指向栈尾的元素的时候,都是在 Option 类型下,方便了功能实现,也方便了错误处理。Option 还有很多强大的功能,读者可以参考下面几个连接: 71 | 72 | [Option标准库文档](http://doc.rust-lang.org/nightly/std/option/enum.Option.html) 73 | 74 | [Error Handling in Rust](http://blog.burntsushi.net/rust-error-handling/) 75 | 76 | [rustbyexample 的 Error handling](https://doc.rust-lang.org/stable/rust-by-example/error.html) 77 | 78 | ## 实现 `new( ) push( ) pop( )` 79 | 接下来是实现 stack 的主要功能了。 80 | 81 | ```rust 82 | impl Stack { 83 | fn new() -> Stack { 84 | Stack{ top: None } 85 | } 86 | 87 | fn push(&mut self, val: T) { 88 | let mut node = StackNode::new(val); 89 | let next = self.top.take(); 90 | node.next = next; 91 | self.top = Some(Box::new(node)); 92 | } 93 | 94 | fn pop(&mut self) -> Option { 95 | let val = self.top.take(); 96 | match val { 97 | None => None, 98 | Some(mut x) => { 99 | self.top = x.next.take(); 100 | Some(x.val) 101 | }, 102 | } 103 | } 104 | } 105 | ``` 106 | 107 | - `new( )`比较简单,Stack 初始化的时候为空,栈顶元素 `top` 就没有任何值,所以 `top` 为 `None`。 108 | 109 | - `push( )`的主要功能是往栈里面推入元素,把新的 StackNode 指向 Stack 里面旧的值,同时更新 Stack 栈顶指向新进来的值。 110 | > 这里有个需要注意的地方是第8行代码里面,`let next = self.top.take();`,使用了 Option 类型的 take 方法: 111 | `fn take(&mut self) -> Option` 112 | 它会把 Option 类型的值取走,并把它的元素改为 None 113 | 114 | - `pop( )`的功能是取出栈顶的元素,如果栈顶为 None 则返回 None。 115 | 116 | ## 完整代码(包含简单的测试) 117 | 118 | ```rust 119 | #[derive(Debug)] 120 | struct Stack { 121 | top: Option>>, 122 | } 123 | 124 | #[derive(Clone,Debug)] 125 | struct StackNode { 126 | val: T, 127 | next: Option>>, 128 | } 129 | 130 | impl StackNode { 131 | fn new(val: T) -> StackNode { 132 | StackNode { val: val, next: None } 133 | } 134 | } 135 | 136 | impl Stack { 137 | fn new() -> Stack { 138 | Stack{ top: None } 139 | } 140 | 141 | fn push(&mut self, val: T) { 142 | let mut node = StackNode::new(val); 143 | let next = self.top.take(); 144 | node.next = next; 145 | self.top = Some(Box::new(node)); 146 | } 147 | 148 | fn pop(&mut self) -> Option { 149 | let val = self.top.take(); 150 | match val { 151 | None => None, 152 | Some(mut x) => { 153 | self.top = x.next.take(); 154 | Some(x.val) 155 | }, 156 | } 157 | } 158 | } 159 | 160 | fn main() { 161 | #[derive(PartialEq,Eq,Debug)] 162 | struct TestStruct { 163 | a: i32, 164 | } 165 | 166 | let a = TestStruct{ a: 5 }; 167 | let b = TestStruct{ a: 9 }; 168 | 169 | let mut s = Stack::<&TestStruct>::new(); 170 | assert_eq!(s.pop(), None); 171 | 172 | s.push(&a); 173 | s.push(&b); 174 | println!("{:?}", s); 175 | 176 | assert_eq!(s.pop(), Some(&b)); 177 | assert_eq!(s.pop(), Some(&a)); 178 | assert_eq!(s.pop(), None); 179 | } 180 | ``` 181 | --------------------------------------------------------------------------------