From 4f78854d06bae6433241681fdb5dadd489f3842a Mon Sep 17 00:00:00 2001 From: twq0076262 Date: Tue, 19 Jul 2016 10:48:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E7=BA=BF=E6=9E=81=E5=AE=A2=E5=AD=A6?= =?UTF-8?q?=E9=99=A2wiki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 toc、config、cover文件与校对格式 --- README.md | 1 + TOC.md | 117 ++++++++++++++++++ action/db/readme.md | 5 + action/json_data/readme.md | 15 +++ action/mysite/readme.md | 16 +++ attr-and-compiler-arg/rustc-option.md | 2 +- cargo-detailed-cfg/cargo-detailed-cfg.md | 11 ++ .../cargo-projects-manager.md | 7 +- closure/overview.md | 1 + closure/syntax.md | 1 + collections/hashmap.md | 2 + collections/vec.md | 2 + .../message-passing.md | 40 +++--- concurrency-parallel-thread/parallel.md | 1 + concurrency-parallel-thread/share-memory.md | 14 ++- concurrency-parallel-thread/synchronize.md | 10 ++ concurrency-parallel-thread/thread.md | 5 + config.json | 4 + cover/background.jpg | Bin 0 -> 9987 bytes cover/logo.png | Bin 0 -> 8819 bytes data-structure/binary_tree.md | 7 ++ data-structure/graph.md | 1 + data-structure/linked_list.md | 2 + data-structure/priority_queue.md | 12 +- data-structure/stack.md | 5 + editors/before.md | 5 + editors/emacs.md | 8 +- editors/spacemacs.md | 6 + editors/sublime.md | 2 - editors/vim.md | 6 +- editors/visualstudio.md | 34 ++--- error-handling/option-result.md | 38 ++++++ ffi/calling-ffi-function.md | 15 +++ ffi/compiling-rust-to-lib.md | 6 + ffi/preface.md | 4 +- flow/repetition.md | 12 ++ function/arguement.md | 6 + function/higher_order_function.md | 18 ++- function/overview.md | 2 + function/return_value.md | 14 ++- function/statement_expression.md | 18 ++- generic/generic.md | 24 +++- {image => images}/0.png | 0 {image => images}/editor-emacs-base.png | Bin {image => images}/editor-emacs-completion.png | Bin .../editor-emacs-error-checking.png | Bin {image => images}/editor-emacs-jump.gif | Bin {image => images}/editor-vim-wayslog.png | Bin {image => images}/editor-vim-welldone.png | Bin ...ditor-visualstudio-GDBproject-settings.png | Bin ...itor-visualstudio-GDBproject-settings2.png | Bin .../editor-visualstudio-GDBproject.png | Bin .../editor-visualstudio-add-files.png | Bin .../editor-visualstudio-autocomplete.png | Bin .../editor-visualstudio-debugging.png | Bin .../editor-visualstudio-debugging2.png | Bin .../editor-visualstudio-download.png | Bin .../editor-visualstudio-newproject.png | Bin .../editor-visualstudio-quickdebug.png | Bin .../editor-visualstudio-racer.png | Bin .../editor-visualstudio-racersc.png | Bin .../editor-visualstudio-set-breakpoints.png | Bin .../editor-visualstudio-setdebugger.png | Bin .../editor-visualstudio-settings.png | Bin {image => images}/function-return-value.png | Bin .../function-statement-expression.png | Bin {image => images}/get-mac-os-information.png | Bin {image => images}/high-order-function.png | Bin .../install-on-linux-check-system.png | Bin .../install-on-linux-rust-success.png | Bin {image => images}/install-on-windows-1st.png | Bin {image => images}/project-structure.png | Bin install/install_rust_on_linux.md | 6 +- install/install_rust_on_mac_os.md | 2 +- install/install_rust_on_windows.md | 4 +- install/rustup.md | 1 + intoborrow/asref.md | 1 + intoborrow/borrow.md | 1 + intoborrow/cow.md | 3 + intoborrow/deref.md | 4 +- intoborrow/into.md | 5 + iterator/iterator.md | 4 + macro/macro.md | 1 + match/match.md | 3 + match/pattern.md | 5 + module/module.md | 12 ++ operator-overloading/operator.md | 6 + ownership-system/borrowing_reference.md | 15 ++- ownership-system/lifetime.md | 5 +- ownership-system/ownership_system.md | 3 + quickstart/comments-document.md | 5 +- quickstart/control-flow.md | 2 + quickstart/function-method.md | 5 + quickstart/io-stream.md | 10 ++ quickstart/primitive-type.md | 6 + quickstart/rust-travel.md | 4 + quickstart/struct-enum.md | 1 + quickstart/trait.md | 3 + quickstart/vector-string.md | 6 + rcarc/cell.md | 5 + rcarc/mutex.md | 1 + rcarc/rcarc.md | 1 + safe/safety.md | 8 +- std/fs-and-path.md | 1 + std/process.md | 1 + testing/bench.md | 6 + testing/threearchtest.md | 6 + trait/trait-object.md | 2 + trait/trait.md | 3 + type/compound-types.md | 9 ++ type/operator-and-formatting.md | 2 +- type/string.md | 5 + type/types.md | 4 + unsafe-rawpointer/raw-pointer.md | 5 + unsafe-rawpointer/unsafe.md | 9 ++ 115 files changed, 620 insertions(+), 70 deletions(-) create mode 100644 TOC.md create mode 100644 config.json create mode 100644 cover/background.jpg create mode 100644 cover/logo.png rename {image => images}/0.png (100%) rename {image => images}/editor-emacs-base.png (100%) rename {image => images}/editor-emacs-completion.png (100%) rename {image => images}/editor-emacs-error-checking.png (100%) rename {image => images}/editor-emacs-jump.gif (100%) rename {image => images}/editor-vim-wayslog.png (100%) rename {image => images}/editor-vim-welldone.png (100%) rename {image => images}/editor-visualstudio-GDBproject-settings.png (100%) rename {image => images}/editor-visualstudio-GDBproject-settings2.png (100%) rename {image => images}/editor-visualstudio-GDBproject.png (100%) rename {image => images}/editor-visualstudio-add-files.png (100%) rename {image => images}/editor-visualstudio-autocomplete.png (100%) rename {image => images}/editor-visualstudio-debugging.png (100%) rename {image => images}/editor-visualstudio-debugging2.png (100%) rename {image => images}/editor-visualstudio-download.png (100%) rename {image => images}/editor-visualstudio-newproject.png (100%) rename {image => images}/editor-visualstudio-quickdebug.png (100%) rename {image => images}/editor-visualstudio-racer.png (100%) rename {image => images}/editor-visualstudio-racersc.png (100%) rename {image => images}/editor-visualstudio-set-breakpoints.png (100%) rename {image => images}/editor-visualstudio-setdebugger.png (100%) rename {image => images}/editor-visualstudio-settings.png (100%) rename {image => images}/function-return-value.png (100%) rename {image => images}/function-statement-expression.png (100%) rename {image => images}/get-mac-os-information.png (100%) rename {image => images}/high-order-function.png (100%) rename {image => images}/install-on-linux-check-system.png (100%) rename {image => images}/install-on-linux-rust-success.png (100%) rename {image => images}/install-on-windows-1st.png (100%) rename {image => images}/project-structure.png (100%) diff --git a/README.md b/README.md index 9703afd..1403dd9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [build-url]: https://travis-ci.org/rustcc/RustPrimer The Rust primer for beginners. + 给初学者的Rust中文教程。 ## 在线阅读地址 diff --git a/TOC.md b/TOC.md new file mode 100644 index 0000000..b2e6e9a --- /dev/null +++ b/TOC.md @@ -0,0 +1,117 @@ +- [初识Rust](1st-glance/README.md) +- [安装Rust](install/preface.md) + - [Linux](install/install_rust_on_linux.md) + - [Mac](install/install_rust_on_mac_os.md) + - [Windows](install/install_rust_on_windows.md) + - [版本管理工具: rustup](install/rustup.md) +- [编辑器](editors/preface.md) + - [前期准备](editors/before.md) + - [vim](editors/vim.md) + - [emacs](editors/emacs.md) + - [vscode](editors/vscode.md) + - [atom](editors/atom.md) + - [sublime](editors/sublime.md) + - [visual studio](editors/visualstudio.md) + - [spacemacs](editors/spacemacs.md) +- [Rust快速入门](quickstart/quickstart.md) + - [Rust旅程](quickstart/rust-travel.md) + - [变量绑定与原生类型](quickstart/primitive-type.md) + - [数组、动态数组和字符串](quickstart/vector-string.md) + - [结构体与枚举](quickstart/struct-enum.md) + - [控制流](quickstart/control-flow.md) + - [函数与方法](quickstart/function-method.md) + - [特性](quickstart/trait.md) + - [注释与文档](quickstart/comments-document.md) + - [输入输出流](quickstart/io-stream.md) +- [Cargo项目管理器](cargo-projects-manager/cargo-projects-manager.md) +- [基本程序结构](flow/preface.md) + - [注释](flow/comment.md) + - [条件](flow/condition.md) + - [循环](flow/repetition.md) +- [类型、运算符和字符串](type/preface.md) + - [基础类型](type/types.md) + - [复合类型](type/compound-types.md) + - [字符串类](type/string.md) + - [基础运算符和字符串格式化](type/operator-and-formatting.md) +- [函数](function/overview.md) + - [函数参数](function/arguement.md) + - [函数返回值](function/return_value.md) + - [语句和表达式](function/statement_expression.md) + - [高阶函数](function/higher_order_function.md) +- [模式匹配](match/overview.md) + - [match关键字](match/match.md) + - [模式 pattern](match/pattern.md) +- [特征 Trait](trait/overview.md) + - [trait关键字](trait/trait.md) + - [trait对象](trait/trait-object.md) +- [泛型](generic/generic.md) +- [可变性、所有权、租借和生命期](ownership-system/ownership_system.md) + - [所有权](ownership-system/ownership.md) + - [引用和借用](ownership-system/borrowing_reference.md) + - [生命周期](ownership-system/lifetime.md) +- [闭包](closure/overview.md) + - [闭包的语法](closure/syntax.md) + - [闭包的实现](closure/implementation.md) + - [闭包作为参数和返回值](closure/as_argument_return_value.md) +- [集合类型 Collections](collections/overview.md) + - [动态数组 Vec](collections/vec.md) + - [哈希表 HashMap](collections/hashmap.md) +- [迭代器](iterator/overview.md) + - [迭代器、适配器、消费者](iterator/iterator.md) +- [模块和包系统、Prelude](module/preface.md) + - [模块 module 和包 crate](module/module.md) + - [Prelude](module/prelude.md) +- [Option、Result与错误处理](error-handling/option-result.md) +- [输入与输出](io/io.md) +- [宏系统](macro/macro.md) +- [堆、栈与Box](heap-stack/heap-stack.md) +- [几种智能指针](rcarc/preface.md) + - [Rc, Arc](rcarc/rcarc.md) + - [Mutex, RwLock](rcarc/mutex.md) + - [Cell, RefCell](rcarc/cell.md) +- [类型系统中的几个常见 Trait](intoborrow/preface.md) + - [Into/From 及其在 String 和 &str 互转上的应用](intoborrow/into.md) + - [AsRef, AsMut](intoborrow/asref.md) + - [Borrow, BorrowMut, ToOwned](intoborrow/borrow.md) + - [Deref 与 Deref coercions](intoborrow/deref.md) + - [Cow 及其在 String 和 &str 上的应用](intoborrow/cow.md) +- [Send 和 Sync](marker/sendsync.md) +- [并发,并行,多线程编程](concurrency-parallel-thread/preface.md) + - [线程](concurrency-parallel-thread/thread.md) + - [消息传递](concurrency-parallel-thread/message-passing.md) + - [共享内存](concurrency-parallel-thread/share-memory.md) + - [同步](concurrency-parallel-thread/synchronize.md) + - [并行](concurrency-parallel-thread/parallel.md) +- [Unsafe、原始指针](unsafe-rawpointer/preface.md) + - [Unsafe](unsafe-rawpointer/unsafe.md) + - [原始指针](unsafe-rawpointer/raw-pointer.md) +- [FFI](ffi/preface.md) + - [rust调用ffi函数](ffi/calling-ffi-function.md) + - [将rust编译成库](ffi/compiling-rust-to-lib.md) +- [运算符重载](operator-overloading/operator.md) +- [属性和编译器参数](attr-and-compiler-arg/preface.md) + - [属性](attr-and-compiler-arg/attribute.md) + - [编译器参数](attr-and-compiler-arg/rustc-option.md) +- [Cargo参数配置](cargo-detailed-cfg/cargo-detailed-cfg.md) +- [测试与评测](testing/preface.md) + - [测试 (testing)](testing/threearchtest.md) + - [评测 (benchmark)](testing/bench.md) +- [代码风格](coding-style/style.md) +- [Any与反射](any/any.md) +- [安全(safe)](safe/safety.md) +- [常用数据结构实现](data-structure/preface.md) + - [栈结构](data-structure/stack.md) + - [队列](data-structure/queue.md) + - [二叉树](data-structure/binary_tree.md) + - [优先队列](data-structure/priority_queue.md) + - [链表](data-structure/linked_list.md) + - [图结构](data-structure/graph.md) +- [标准库介绍](std/overview.md) + - [系统命令:调用grep](std/process.md) + - [目录操作:简单grep](std/fs-and-path.md) + - [网络模块:W回音](std/net.md) +- [实战篇](action/preface.md) + - [实战:Json处理](action/json_data/readme.md) + - [实战:Web 应用开发入门](action/mysite/readme.md) + - [实战:使用Postgresql数据库](action/db/readme.md) +- [附录-术语表](appendix/glossary.md) diff --git a/action/db/readme.md b/action/db/readme.md index b38233e..63fca1e 100644 --- a/action/db/readme.md +++ b/action/db/readme.md @@ -142,6 +142,7 @@ pub fn query_all(conn: &Connection,query: &str){ } ``` + 然后在main.rs 中调用相应的函数代码如下 1. extern db ,引入db,也就是将项目本身引入 2. use db 使用db,中的可以被引入的函数 @@ -182,6 +183,7 @@ fn main() { ``` 自己遇到的坑 + - 创建连接函数时,连接必须有一个返回值,所以必须指定返回值的类型, 对于一个写python的人而言,我觉得是痛苦的,我想按照官方的写法match 一下,发现可能产生多个返回值。在编译时直接无法通过编译,所以最终 @@ -202,7 +204,9 @@ pub fn query_all(conn: &Connection,query: &str){ } ``` + 报错如下: + ``` rust vagrant@ubuntu-14:~/tmp/test/rustprimer/db$ cargo run Compiling db v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/db) @@ -217,6 +221,7 @@ error: aborting due to previous error Could not compile `db`. ``` + 然后去查看了关于postgres模块的所有函数,尝试了无数种办法,依旧没有解决。 可能自己眼高手低,如果从头再把rust的相关教程看一下,可能很早就发现这个问题, diff --git a/action/json_data/readme.md b/action/json_data/readme.md index f7f8a3b..4007a62 100644 --- a/action/json_data/readme.md +++ b/action/json_data/readme.md @@ -5,10 +5,13 @@ json是一种比较重要的格式,尤其是现在的web开发领域,json相 rust中的json处理依赖 cargo 中的rustc-serialize模块 ###先简单的创建一个rust项目工程 + ``` rust $ cargo new json_data --bin ``` + 生成文件树: + ```shell vagrant@ubuntu-14:~/tmp/test/rustprimer$ tree . @@ -19,6 +22,7 @@ vagrant@ubuntu-14:~/tmp/test/rustprimer$ tree ``` + 生成项目`json_data`,项目下文件介绍: - Caogo.toml ,文件中填写一些项目的相关信息,比如版本号,联系人,项目名,文件的内容如下: @@ -39,6 +43,7 @@ authors = ["wangxxx "] rustc-serialize 这个是第三方的模块,需要从[cargo](https://crates.io/crates/rustc-serialize)下载。 下载很简单,只需修改一下cargo.toml文件就行了. + ```toml [package] name = "json_data" @@ -49,12 +54,16 @@ authors = ["wangxxx "] rustc-serialize = "0.3.18" ``` + 然后执行在当前目录执行: + ``` $ cargo build ``` + *注意一个问题由于国内网络访问github不稳定,这些第三方库很多托管在github上,所以可能需要修改你的 网络访问* + 1. 在安装rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹 2. 在`.cargo`文件夹下,创建一个`config`文件,在文件中填写中科大软件源,可能以后会出现其他的源,先用这个 3. `config`文件内容如下 @@ -64,6 +73,7 @@ $ cargo build index = "git://crates.mirrors.ustc.edu.cn/index" ``` + cargo build 执行之后的提示信息 ``` @@ -72,6 +82,7 @@ cargo build 执行之后的提示信息 Compiling rustc-serialize v0.3.18 (registry git://crates.mirrors.ustc.edu.cn/index) Compiling json_data v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/json_data) ``` + 再次执行tree命令: ``` @@ -90,6 +101,7 @@ cargo build 执行之后的提示信息 `-- native ``` + 可以看到多了很多文件,重点关注`cargo.lock`,开打文件: ```toml @@ -106,9 +118,11 @@ version = "0.3.18" source = "registry+git://crates.mirrors.ustc.edu.cn/index" ``` + 是关于项目编译的一些依赖信息 还有生成了target文件夹,生成了可执行文件json_data,因为main.rs中的执行结果就是打印`hello world` + ``` $ cargo run @@ -152,4 +166,5 @@ fn main() { } ``` + 当然我们也可以在文本中作为api的返回结果使用,下来的章节中,我们将讨论这个问题 diff --git a/action/mysite/readme.md b/action/mysite/readme.md index dc80153..d48c9c1 100644 --- a/action/mysite/readme.md +++ b/action/mysite/readme.md @@ -10,10 +10,12 @@ rust目前比较有名的框架是iron和nickel,我们两个都写一下简单 接上一篇,使用cargo获取第三方库。`cargo new mysite --bin` 在cargo.toml中添加iron的依赖, + ```toml [dependencies] iron = "*" ``` + 然后build将依赖下载到本地 `cargo build` 如果报ssl错误,那可能你需要安装linux的ssl开发库。 @@ -32,6 +34,7 @@ fn main() { }).http("localhost:3000").unwrap(); } ``` + 然后运行 `cargo run` @@ -53,6 +56,7 @@ let plus_one = |x: i32| x + 1; assert_eq!(2, plus_one(1)); ``` + 具体的怎么使用 ,可以暂时不用理会,因为你只要知道如何完成web,因为我也不会。。 结合之前一章节的json处理,我们来看看web接口怎么返回json,当然也要 rustc_serialize 放到 cargo.toml 中 @@ -82,12 +86,14 @@ fn main() { println!("On 3000"); } ``` + 执行 cargo run 使用 curl 测试结果: ``` curl localhost:3000 {"msg":"Hello, World"} ``` + 当然可以可以实现更多的业务需求,通过控制自己的json。 既然有了json了,如果要多个路由什么的,岂不是完蛋了,所以不可能这样的,我们需要考虑一下怎么实现路由的定制 @@ -133,8 +139,10 @@ fn main() { Iron::new(router).http("localhost:3000").unwrap(); } ``` + 这次添加了路由的实现和获取客户端发送过来的数据,有了get,post,所以现在一个基本的api网站已经完成了。不过 并不是所有的网站都是api来访问,同样需要html模版引擎和直接返回静态页面。等等 + ``` vagrant@ubuntu-14:~/tmp/test/rustprimer/mysite$ cargo build Compiling mysite v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/mysite) @@ -146,6 +154,7 @@ src/main.rs:29:36: 29:52 help: candidate #1: use `std::io::Read` error: aborting due to previous error Could not compile `mysite`. ``` + 编译出错了,太糟糕了,提示说没有read_to_string这个方法,然后我去文档查了一下,发现有[read_to_string方法](http://ironframework.io/doc/iron/request/struct.Body.html) 再看提示信息 @@ -201,6 +210,7 @@ let mut buffer = String::new(); try!(f.read_to_string(&mut buffer)); ``` + 用法比较简单,我们修改一下刚刚的函数: ``` @@ -213,12 +223,14 @@ fn set_greeting(request: &mut Request) -> IronResult { Ok(Response::with((status::Ok, payload))) } ``` + 从request中读取字符串,读取的结果存放到payload中,然后就可以进行操作了,编译之后运行,使用curl提交一个post数据 ``` $curl -X POST -d '{"msg":"Just trust the Rust"}' http://localhost:3000/set {"msg":"Just trust the Rust"} ``` + iron 基本告一段落 当然还有如何使用html模版引擎,那就是直接看文档就行了。 @@ -247,6 +259,7 @@ fn main() { server.listen("127.0.0.1:6767"); } ``` + 简单来看,也就是这样回事。 1. 引入了nickel的宏 @@ -276,18 +289,21 @@ fn main() { } ``` + 上面的信息你可以编译,使用curl看看发现出现 ``` $ curl http://127.0.0.1:6767 Internal Server Error ``` + 看看文档,没发现什么问题,我紧紧更换了一个文件夹的名字,这个文件夹我也创建了。 然后我在想难道是服务器将目录写死了吗?于是将上面的路径改正这个,问题解决。 ```rust return response.render("examples/assets/template.tpl", &data); ``` + 我们看一下目录结构 ``` diff --git a/attr-and-compiler-arg/rustc-option.md b/attr-and-compiler-arg/rustc-option.md index 9610f21..26df043 100644 --- a/attr-and-compiler-arg/rustc-option.md +++ b/attr-and-compiler-arg/rustc-option.md @@ -1,4 +1,4 @@ -## 编译器参数 +# 编译器参数 本章将介绍Rust编译器的参数。 diff --git a/cargo-detailed-cfg/cargo-detailed-cfg.md b/cargo-detailed-cfg/cargo-detailed-cfg.md index 8cd27b0..586860b 100644 --- a/cargo-detailed-cfg/cargo-detailed-cfg.md +++ b/cargo-detailed-cfg/cargo-detailed-cfg.md @@ -1,5 +1,6 @@ 筒子们好,我们又见面了。之前第5章,我们一起探讨了cargo的一些常用的基本技能。通过第5章的学习,大家基本能解决日常项目开发中遇到的大多数问题。但实际上,cargo提供给我们所使用的功能不仅限于此。我只想说一个字:cargo很好很强大,而且远比你想象的强大。 本章将深入探讨cargo的一些细节问题,这包括以下几个方面: + - 基于语义化版本的项目版本声明与管理 - cargo的toml描述文件配置字段详细参考 @@ -15,6 +16,7 @@ version = "0.1.0" libc = "0.2" ``` + 这里package段落中的version字段的值,以及dependencies段落中的libc字段的值,这些值的写法,都涉及到语义化版本控制的问题。语义化版本控制是用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。简单来说,语义化版本控制遵循下面这些规则: - 版本格式:主版本号.次版本号.修订号,版本号递增规则如下: @@ -28,8 +30,10 @@ libc = "0.2" 关于语义化版本控制的具体细节问题,大家可以参考[这里](http://semver.org/lang/zh-CN/),我不再赘述。 # cargo的toml描述文件配置字段详细参考 + ## [package]段落 啥也不多说了,直接上例子,大家注意我在例子中的中文解释,个人觉得这样比较一目了然: + ```toml [package] # 软件包名称,如果需要在别的地方引用此软件包,请用此名称。 @@ -78,6 +82,7 @@ license-file = "..." ## 依赖的详细配置 最直接的方式在之前第五章探讨过,这里不在赘述,例如这样: + ```toml [dependencies] hammer = "0.5.0" @@ -177,6 +182,7 @@ debug-assertions = true codegen-units = 1 ``` + 需要注意的是,当调用编译器时,只有位于调用最顶层的软件包的模板文件有效,其他的子软件包或者依赖软件包的模板定义将被顶层软件包的模板覆盖。 ## [features]段落 @@ -217,13 +223,16 @@ civet = { version = "*", optional = true } ``` 如果其他软件包要依赖使用上述awesome软件包,可以在其描述文件中这样写: + ```toml [dependencies.awesome] version = "1.3.5" default-features = false # 禁用awesome 的默认features features = ["secure-password", "civet"] # 使用此处列举的各项features ``` + 使用features时需要遵循以下规则: + - feature名称在本描述文件中不能与出现的软件包名称冲突 - 除了default feature,其他所有的features均是可选的 - features不能相互循环包含 @@ -235,8 +244,10 @@ features的一个重要用途就是,当开发者需要对软件包进行最终 ``` $ cargo build --release --features "shumway pdf" ``` + ## 关于测试 当运行cargo test命令时,cargo将会按做以下事情: + - 编译并运行软件包源代码中被#[cfg(test)] 所标志的单元测试 - 编译并运行文档测试 - 编译并运行集成测试 diff --git a/cargo-projects-manager/cargo-projects-manager.md b/cargo-projects-manager/cargo-projects-manager.md index fb2b984..e95b3e0 100644 --- a/cargo-projects-manager/cargo-projects-manager.md +++ b/cargo-projects-manager/cargo-projects-manager.md @@ -1,10 +1,12 @@ # cargo简介 + 曾几何时,对于使用惯了`C/C++`语言的猿们来说,项目代码的组织与管理绝对是一场噩梦。为了解决`C/C++`项目的管理问题,猿神们想尽了各种办法,开发出了各种五花八门的项目管理工具,从一开始的`automake`到后来的`cmake`、`qmake`等等,但结果并不如人意,往往是解决了一些问题,却引入了更多的问题,`C/C++`猿们经常会陷入在掌握语言本身的同时,还要掌握复杂的构建工具语法的窘境。无独有偶,`java`的项目代码组织与管理工具`ant`和`maven`也存在同样的问题。复杂的项目管理配置参数,往往让猿们不知所措。 作为一门现代语言,`rust`自然要摒弃石器时代项目代码管理的方法和手段。`rust`项目组为各位猿提供了超级大杀器`cargo`,以解决项目代码管理所带来的干扰和困惑。用过`node.js`的猿们,应该对`node.js`中的神器`npm`、`grunt`、`gulp`等工具印象深刻。作为新一代静态语言中的翘楚,`rust`官方参考了现有语言管理工具的优点,于是就产生了`cargo`。 言而总之,作为`rust`的代码组织管理工具,`cargo`提供了一系列的工具,从项目的建立、构建到测试、运行直至部署,为`rust`项目的管理提供尽可能完整的手段。同时,与`rust`语言及其编译器`rustc`本身的各种特性紧密结合,可以说既是语言本身的知心爱人,又是`rust`猿们的贴心小棉袄,谁用谁知道。 废话就不多说了,直接上例子和各种高清无马图。 + # cargo入门 首先,当然还是废话,要使用cargo,自然首先要安装cargo。安装cargo有三种方法,前两种方法请参见rust的安装方法,因为cargo工具是官方正统出身,当然包含在官方的分发包中。第三种方法即从[`cargo`](https://github.com/rust-lang/cargo)项目的源码仓库进行构建。Oh,My God。的确是废话。 @@ -89,6 +91,7 @@ authors = ["fuying"] [dependencies] ``` + toml文件是由诸如[package]或[dependencies]这样的段落组成,每一个段落又由多个字段组成,这些段落和字段就描述了项目组织的基本信息,例如上述toml文件中的[package]段落描述了`hello_world`项目本身的一些信息,包括项目名称(对应于name字段)、项目版本(对应于version字段)、作者列表(对应于authors字段)等;[dependencies]段落描述了`hello_world`项目的依赖项目有哪些。 下面我们来看看toml描述文件中常用段落和字段的意义。 @@ -119,6 +122,7 @@ geometry = { path = "crates/geometry" } cargo另一个重要的功能,即将软件开发过程中必要且非常重要的测试环节进行集成,并通过代码属性声明或者toml文件描述来对测试进行管理。其中,单元测试主要通过在项目代码的测试代码部分前用`#[test]`属性来描述,而集成测试,则一般都会通过toml文件中的[[test]]段落进行描述。 例如,假设集成测试文件均位于tests文件夹下,则toml可以这样来写: + ```toml [[test]] name = "testinit" @@ -127,8 +131,8 @@ path = "tests/testinit.rs" [[test]] name = "testtime" path = "tests/testtime.rs" - ``` + 上述例子中,name字段定义了集成测试的名称,path字段定义了集成测试文件相对于本toml文件的路径。 看看,定义集成测试就是如此简单。 需要注意的是: @@ -149,6 +153,7 @@ name = "bin1" path = "bin/bin1.rs" ``` + 对于'[[example]]'和'[[bin]]'段落中声明的examples和bins,需要通过'cargo run --example NAME'或者'cargo run --bin NAME'来运行,其中NAME对应于你在name字段中定义的名称。 # 构建、清理、更新以及安装 diff --git a/closure/overview.md b/closure/overview.md index 05fc8f5..a485071 100644 --- a/closure/overview.md +++ b/closure/overview.md @@ -1,5 +1,6 @@ # 闭包 闭包是什么?先来看看[维基百科][wiki]上的描述: + >在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是 __引用了自由变量的函数__。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

闭包的概念出现于60年代,最早实现闭包的程序语言是Scheme。之后,闭包被广泛使用于函数式编程语言如ML语言和LISP。很多命令式程序语言也开始支持闭包。 diff --git a/closure/syntax.md b/closure/syntax.md index 4d7398b..197718f 100644 --- a/closure/syntax.md +++ b/closure/syntax.md @@ -157,4 +157,5 @@ assert_eq!(5, num); 它就不能继续在原先的函数中使用了,只能在闭包内使用。 不过在我们讨论获取或返回闭包之前,我们应该更多的了解一下闭包实现的方法。作为一个系统语言,Rust给予你了大量的控制你代码的能力,而闭包也是一样。 + > ### 这部分引用自[The Rust Programming Language中文版](https://github.com/KaiserY/rust-book-chinese/blob/master/content/Closures%20%E9%97%AD%E5%8C%85.md) diff --git a/collections/hashmap.md b/collections/hashmap.md index 12092e1..a992e8e 100644 --- a/collections/hashmap.md +++ b/collections/hashmap.md @@ -17,6 +17,7 @@ 这样,即可将你的类型转换成一个可以作为Hash的Key的类型。 但是,如果你想要自己实现`Hash`这个trait的话,你需要谨记两点: + * 1. 如果 Key1==Key2 ,那么一定有 Hash(Key1) == Hash(Key2) * 2. 你的Hash函数本身不能改变你的Key值,否则将会引发一个逻辑错误(很难排查,遇到就完的那种) @@ -66,6 +67,7 @@ for (name, location) in &come_from { ``` 这段代码输出: + ``` Oh, 我们查到了3个人,但是可怜的Elton猫还是无家可归 Mike猫的家乡不是火锅!不是火锅!不是火锅!虽然好吃! diff --git a/collections/vec.md b/collections/vec.md index ab774a0..085c4d3 100644 --- a/collections/vec.md +++ b/collections/vec.md @@ -59,6 +59,7 @@ let v: Vec<_> = (1..5).collect(); 就像数组一样,因为Vec借助`Index`和`IndexMut`提供了随机访问的能力,我们通过`[index]`来对其进行访问,当然,既然存在随机访问就会出现越界的问题。而在Rust中,一旦越界的后果是极其严重的,可以导致Rust当前线程panic。因此,除非你确定自己在干什么或者在`for`循环中,不然我们不推荐通过下标访问。 以下是例子: + ```rust let a = vec![1, 2, 3]; assert_eq!(a[1usize], 2); @@ -125,6 +126,7 @@ time spend: Duration { secs: 0, nanos: 259878787 } ./demo 0.62s user 0.01s system 99% cpu 0.632 total ``` + 好像并没有太大差异?然而切换到release版本的时候: ``` diff --git a/concurrency-parallel-thread/message-passing.md b/concurrency-parallel-thread/message-passing.md index 1770086..12356fb 100644 --- a/concurrency-parallel-thread/message-passing.md +++ b/concurrency-parallel-thread/message-passing.md @@ -25,24 +25,28 @@ fn main() { println!("receive {}", rx.recv().unwrap()); } ``` + 程序说明参见代码中的注释,程序执行结果为: + ``` receive 1 ``` + 结果表明`main`所在的主线程接收到了新建线程发送的消息,用Rust在线程间传递消息就是这么简单! 虽然简单,但使用过其他语言就会知道,通道有多种使用方式,且比较灵活,为此我们需要进一步考虑关于`Rust`的`Channel`的几个问题: - 1. 通道能保证消息的顺序吗?是否先发送的消息,先接收? - 2. 通道能缓存消息吗?如果能的话能缓存多少? - 3. 通道的发送者和接收者支持N:1,1:N, N:N模式吗? - 4. 通道能发送任何数据吗? - 5. 发送后的数据,在线程中继续使用没有问题吗? +1. 通道能保证消息的顺序吗?是否先发送的消息,先接收? +2. 通道能缓存消息吗?如果能的话能缓存多少? +3. 通道的发送者和接收者支持N:1,1:N, N:N模式吗? +4. 通道能发送任何数据吗? +5. 发送后的数据,在线程中继续使用没有问题吗? 让我们带着这些问题和思考进入下一个小节,那里有相关的答案。 ### 消息类型 上面的例子中,我们传递的消息类型为`i32`,除了这种类型之外,是否还可以传递更多的原始类型,或者更复杂的类型,和自定义类型?下面我们尝试发送一个更复杂的`Rc`类型的消息: + ```rust use std::fmt; use std::sync::mpsc; @@ -76,28 +80,31 @@ fn main() { println!("receive {}", rx.recv().unwrap()); } ``` + 编译代码,奇迹没有出现,编译时错误,错误提示: + ``` error: the trait `core::marker::Send` is not implemented for the type `alloc::rc::Rc` [E0277] note: `alloc::rc::Rc` cannot be sent between threads safely ``` + 看来并不是所有类型的消息都可以通过通道发送,消息类型必须实现`marker trait Send`。Rust之所以这样强制要求,主要是为了解决并发安全的问题,再一次强调,**安全**是Rust考虑的重中之重。如果一个类型是`Send`,则表明它可以在线程间安全的转移所有权(`ownership`),当所有权从一个线程转移到另一个线程后,同一时间就只会存在一个线程能访问它,这样就避免了数据竞争,从而做到线程安全。`ownership`的强大又一次显示出来了。通过这种做法,在编译时即可要求所有的代码必须满足这一约定,这种方式方法值得借鉴,`trait`也是非常强大。 看起来问题得到了完美的解决,然而由于`Send`本身是一个不安全的`marker trait`,并没有实际的`API`,所以实现它很简单,但没有强制保障,就只能靠开发者自己约束,否则还是可能引发并发安全问题。对于这一点,也不必太过担心,因为Rust中已经存在的类,都已经实现了`Send`或`!Send`,我们只要使用就行。`Send`是一个默认应用到所有Rust已存在类的trait,所以我们用`!Send`显式标明该类没有实现`Send`。目前几乎所有的原始类型都是`Send`,例如前面例子中发送的`i32`。对于开发者而言,我们可能会更关心哪些是非`Send`,也就是实现了`!Send`,因为这会导致线程不安全。更全面的信息参见[`Send`官网API](https://doc.rust-lang.org/std/marker/trait.Send.html)。 对于不是`Send`的情况(`!Send`),大致分为两类: - 1. 原始指针,包括`*mut T`和`*const T`,因为不同线程通过指针都可以访问数据,从而可能引发线程安全问题。 - 2. `Rc`和`Weak`也不是,因为引用计数会被共享,但是并没有做并发控制。 +1. 原始指针,包括`*mut T`和`*const T`,因为不同线程通过指针都可以访问数据,从而可能引发线程安全问题。 +2. `Rc`和`Weak`也不是,因为引用计数会被共享,但是并没有做并发控制。 虽然有这些`!Send`的情况,但是逃不过编译器的火眼金睛,只要你错误地使用了消息类型,编译器都会给出类似于上面的错误提示。我们要担心的不是这些,因为错误更容易出现在新创建的自定义类,有下面两点需要注意: - 1. 如果自定义类的所有字段都是`Send`,那么这个自定义类也是`Send`。 +1. 如果自定义类的所有字段都是`Send`,那么这个自定义类也是`Send`。 反之,如果有一个字段是`!Send`,那么这个自定义类也是`!Send`。 如果类的字段存在递归包含的情况,按照该原则以此类推来推论类是`Send`还是`!Send`。 - 2. 在为一个自定义类实现`Send`或者`!Send`时,必须确保符合它的约定。 +2. 在为一个自定义类实现`Send`或者`!Send`时,必须确保符合它的约定。 到此,消息类型的相关知识已经介绍完了,说了这么久,也该让大家自己练习一下了:请实现一个自定义类,该类包含一个Rc字段,让这个类变成可以在通道中发送的消息类型。 @@ -134,7 +141,9 @@ fn main() { } } ``` + 运行结果: + ``` send 1 send 2 @@ -145,13 +154,13 @@ receive 2 在代码中,我们故意让`main`所在的主线程睡眠2秒,从而让发送者所在线程优先执行,通过结果可以发现,发送者发送消息时确实没有阻塞。还记得在前面提到过很多关于通道的问题吗?从这个例子里面还发现什么没?除了不阻塞之外,我们还能发现另外的三个特征: - 1. 通道是可以同时支持多个发送者的,通过`clone`的方式来实现。 +1.通道是可以同时支持多个发送者的,通过`clone`的方式来实现。 这类似于`Rc`的共享机制。 其实从`Channel`所在的库名`std::sync::mpsc`也可以知道这点。 因为`mpsc`就是多生产者单消费者(Multiple Producers Single Consumer)的简写。 可以有多个发送者,但只能有一个接收者,即支持的N:1模式。 - 2. 异步通道具备消息缓存的功能,因为1和2是在没有接收之前就发了的,在此之后还能接收到这两个消息。 +2.异步通道具备消息缓存的功能,因为1和2是在没有接收之前就发了的,在此之后还能接收到这两个消息。 那么通道到底能缓存多少消息?在理论上是无穷的,尝试一下便知: @@ -186,9 +195,10 @@ fn main() { println!("receive {}", rx.recv().unwrap()); } ``` + 最后的结果就是耗费内存为止。 - 3. 消息发送和接收的顺序是一致的,满足先进先出原则。 +3.消息发送和接收的顺序是一致的,满足先进先出原则。 上面介绍的内容大多是关于发送者和通道的,下面开始考察一下接收端。通过上面的几个例子,细心一点的可能已经发现接收者的`recv`方法应该会阻塞当前线程,如果不阻塞,在多线程的情况下,发送的消息就不可能接收完全。所以没有发送者发送消息,那么接收者将会一直等待,这一点要谨记。在某些场景下,一直等待是符合实际需求的。但某些情况下并不需一直等待,那么就可以考虑释放通道,只要通道释放了,`recv`方法就会立即返回。 @@ -221,7 +231,9 @@ fn main() { new_thread.join().unwrap(); } ``` + 运行结果: + ``` before sleep before send @@ -232,8 +244,8 @@ after send 除了多了一些输出代码之外,上面这段代码几乎和前面的异步通道的没有什么区别,唯一不同的在于创建同步通道的那行代码。同步通道是`sync_channel`,对应的发送者也变成了`SyncSender`。为了显示出同步通道的区别,故意添加了一些打印。和异步通道相比,存在两点不同: - 1. 同步通道是需要指定缓存的消息个数的,但需要注意的是,最小可以是0,表示没有缓存。 - 2. 发送者是会被阻塞的。当通道的缓存队列不能再缓存消息时,发送者发送消息时,就会被阻塞。 +1. 同步通道是需要指定缓存的消息个数的,但需要注意的是,最小可以是0,表示没有缓存。 +2. 发送者是会被阻塞的。当通道的缓存队列不能再缓存消息时,发送者发送消息时,就会被阻塞。 对照上面两点和运行结果来分析,由于主线程在接收消息前先睡眠了,从而子线程这个时候会被调度执行发送消息,由于通道能缓存的消息为0,而这个时候接收者还没有接收,所以`tx.send(1).unwrap()`就会阻塞子线程,直到主线程接收消息,即执行`println!("receive {}", rx.recv().unwrap());`。运行结果印证了这点,要是没阻塞,那么在`before send`之后就应该是`after send`了。 diff --git a/concurrency-parallel-thread/parallel.md b/concurrency-parallel-thread/parallel.md index a1e2ac3..3fd3d17 100644 --- a/concurrency-parallel-thread/parallel.md +++ b/concurrency-parallel-thread/parallel.md @@ -31,6 +31,7 @@ fn main() { ``` 运行结果: + ``` original: [-20, 0, 20, 40, 80, 100, 150, 180, 200, 250, 300] transformed: [0, 0, 0.078431375, 0.15686275, 0.3137255, 0.39215687, 0.5882353, 0.7058824, 0.78431374, 0.98039216, 1] diff --git a/concurrency-parallel-thread/share-memory.md b/concurrency-parallel-thread/share-memory.md index 1148cc9..0673994 100644 --- a/concurrency-parallel-thread/share-memory.md +++ b/concurrency-parallel-thread/share-memory.md @@ -20,11 +20,14 @@ fn main() { println!("static value in main thread: {}", VAR); } ``` + 运行结果: + ``` static value in new thread: 5 static value in main thread: 5 ``` + `VAR`这个`static`变量在各线程中可以直接使用,非常方便。当然上面只是读取,那么要修改也是很简单的: ```rust @@ -48,11 +51,14 @@ fn main() { } } ``` + 运行结果: + ``` static value in new thread: 5 static value in main thread: 6 ``` + 从结果来看`VAR`的值变了,从代码上来看,除了在`VAR`变量前面加了`mut`关键字外,更加明显的是在使用`VAR`的地方都添加了`unsafe`代码块。为什么?所有的线程都能访问`VAR`,且它是可以被修改的,自然就是不安全的。上面的代码比较简单,同一时间只会有一个线程读写`VAR`,不会有什么问题,所以用`unsafe`来标记就可以。如果是更多的线程,还是请使用接下来要介绍的同步机制来处理。 static如此,那const呢? const会在编译时内联到代码中,所以不会存在某个固定的内存地址上,也不存在可以修改的情况,并不是内存共享的。 @@ -80,11 +86,14 @@ fn main() { println!("share value in main thread: {}, address: {:p}", var, &*var); } ``` + 运行结果: + ``` share value in new thread: 5, address: 0x2825070 share value in main thread: 5, address: 0x2825070 ``` + 你可能会觉得很奇怪,上面怎么没有看到Box创建的变量啊,这明明就是`Arc`的使用呀?`Box`创建的变量要想在多个线程中安全使用,我们还需要实现很多功能才行,需要是`Sync`,而`Arc`正是利用`Box`来实现的一个通过引用计数来共享状态的包裹类。下面引用一段`Arc::new`的源码即可看出它是通过`Box`来实现的: ```rust @@ -99,12 +108,13 @@ pub fn new(data: T) -> Arc { Arc { _ptr: unsafe { NonZero::new(Box::into_raw(x)) } } } ``` + 通过上面的运行结果,我们也可以发现新建线程和主线程中打印的`address`是一样的,说明状态确实是在同一个内存地址处。 如果`Box`在堆上分配的资源仅在一个线程中使用,那么释放时,就非常简单,使用完,及时释放即可。如果是要在多个线程中使用,就需要面临两个关键问题: - 1. 资源何时释放? - 2. 线程如何安全的并发修改和读取? +1. 资源何时释放? +2. 线程如何安全的并发修改和读取? 由于上面两个问题的存在,这就是为什么我们不能直接用`Box`变量在线程中共享的原因,可以看出来,共享内存比消息传递机制似乎要复杂许多。Rust用了引用计数的方式来解决第一个问题,在标准库中提供了两个包裹类,除了上面一个用于多线程的`std::sync::Arc`之外,还有一个不能用于多线程的`std::rc::Rc`。在使用时,可以根据需要进行选择。如果你一不小心把`std::rc::Rc`用于多线程中,编译器会毫不客气地纠正你的。 diff --git a/concurrency-parallel-thread/synchronize.md b/concurrency-parallel-thread/synchronize.md index 47c931a..fda714d 100644 --- a/concurrency-parallel-thread/synchronize.md +++ b/concurrency-parallel-thread/synchronize.md @@ -54,12 +54,15 @@ fn main() { } } ``` + 运行结果: + ``` before wait notify main thread after wait ``` + 这个例子展示了如何通过条件变量和锁来控制新建线程和主线程的同步,让主线程等待新建线程执行后,才能继续执行。从结果来看,功能上是实现了。对于上面这个例子,还有下面几点需要说明: * `Mutex`是Rust中的一种锁。 @@ -93,11 +96,14 @@ fn main() { println!("share value in main thread: {}", var.load(Ordering::SeqCst)); } ``` + 运行结果: + ``` share value in new thread: 5 share value in main thread: 9 ``` + 结果表明新建线程成功的修改了值,并在主线程中获取到了最新值,你也可以尝试使用其他的原子类型。此处我们可以思考一下,如果我们用`Arc::new(*mut Box)`是否也可以做到? 为什么? 思考后,大家将体会到Rust在多线程安全方面做的有多么的好。除了原子类型,我们还可以使用锁来实现同样的功能。 ### 锁 @@ -124,11 +130,14 @@ fn main() { println!("share value in main thread: {}", *(var.lock().unwrap())); } ``` + 运行结果: + ``` share value in new thread: 5 share value in main thread: 9 ``` + 结果都一样,看来用`Mutex`也能实现,但如果从效率上比较,原子类型会更胜一筹。暂且不论这点,我们从代码里面看到,虽然有`lock`,但是并么有看到有类似于`unlock`的代码出现,并不是不需要释放锁,而是Rust为了提高安全性,已然在`val`销毁的时候,自动释放锁了。同时我们发现,为了修改共享的值,开发者必须要调用`lock`才行,这样就又解决了一个安全问题。不得不再次赞叹一下Rust在多线程方面的安全性做得真是太好了。如果是其他语言,我们要做到安全,必然得自己来实现这些。 为了保障锁使用的安全性问题,Rust做了很多工作,但从效率来看还不如原子类型,那么锁是否就没有存在的价值了?显然事实不可能是这样的,既然存在,那必然有其价值。它能解决原子类型锁不能解决的那百分之十的问题。我们再来看一下之前的一个例子: @@ -161,6 +170,7 @@ fn main() { } } ``` + 代码中的`Condvar`就是条件变量,它提供了`wait`方法可以主动让当前线程等待,同时提供了`notify_one`方法,让其他线程唤醒正在等待的线程。这样就能完美实现顺序控制了。看起来好像条件变量把事都做完了,要`Mutex`干嘛呢?为了防止多个线程同时执行条件变量的`wait`操作,因为条件变量本身也是需要被保护的,这就是锁能做,而原子类型做不到的地方。 在Rust中,`Mutex`是一种独占锁,同一时间只有一个线程能持有这个锁。这种锁会导致所有线程串行起来,这样虽然保证了安全,但效率并不高。对于写少读多的情况来说,如果在没有写的情况下,都是读取,那么应该是可以并发执行的,为了达到这个目的,几乎所有的编程语言都提供了一种叫读写锁的机制,Rust中也存在,叫[`std::sync::RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html),在使用上同`Mutex`差不多,在此就留给大家自行练习了。 diff --git a/concurrency-parallel-thread/thread.md b/concurrency-parallel-thread/thread.md index 6b5a215..2917f07 100644 --- a/concurrency-parallel-thread/thread.md +++ b/concurrency-parallel-thread/thread.md @@ -26,6 +26,7 @@ fn main() { new_thread.join().unwrap(); } ``` + 执行上面这段代码,将会看到下面的输出结果: ``` @@ -79,6 +80,7 @@ fn main() { new_thread.join().unwrap(); } ``` + 线程就永远都不会结束,如果你用的还是古董电脑,运行上面的代码之前,请做好心理准备。在实际代码中,要时刻警惕该情况的出现(单核情况下,CPU占用率会飙升到100%),除非你是故意为之。 线程结束的另一种方式就是,线程所在进程结束了。我们把上面这个例子稍作修改: @@ -122,13 +124,16 @@ fn main() { thread::sleep_ms(100); } ``` + 这次我们在新建线程中还创建了一个线程,从而第一个新建线程是父线程,主线程在等待该父线程结束后,主动睡眠一段时间。这样做有两个目的,一是确保整个程序不会马上结束;二是如果子线程还存在,应该会获得执行机会,以此来检验子线程是否还在运行,下面是输出结果: + ``` Child thread is finish! I am a new thread. I am a new thread. ...... ``` + 结果表明,在父线程结束后,其创建的子线程还活着,这并不会因为父线程结束而结束。这个还是比较符合自然规律的,要不然真会断子绝孙,人类灭绝。所以导致线程结束的第二种方式,是结束其所在进程。到此为止,我们已经把线程的创建和结束都介绍完了,那么接下来我们会介绍一些更有趣的东西。但是在此之前,请先考虑一下下面的练习题。 **练习题:** diff --git a/config.json b/config.json new file mode 100644 index 0000000..55ae60a --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "name": "Rust 中文教程", + "introduction": "适合初学者的Rust中文教程" +} \ No newline at end of file diff --git a/cover/background.jpg b/cover/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2aa23a3a81fdffd879020ab23e7a4347832b19ed GIT binary patch literal 9987 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 P`wNTSUs(K?eqr%%Pulzn literal 0 HcmV?d00001 diff --git a/cover/logo.png b/cover/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..df03b8adea34f2687ec8fa07a2f84c403f81c54b GIT binary patch literal 8819 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 b|HXt@*i#e_hB;_JULTT|QIf8aGzs}1dip(c literal 0 HcmV?d00001 diff --git a/data-structure/binary_tree.md b/data-structure/binary_tree.md index 006acaf..e523d1d 100644 --- a/data-structure/binary_tree.md +++ b/data-structure/binary_tree.md @@ -19,6 +19,7 @@ ## 定义二叉树的结构 二叉树的每个节点由键key、值value与左右子树left/right组成,这里我们把节点声明为一个泛型结构。 + ```rust type TreeNode = Option>>; #[derive(Debug)] @@ -29,8 +30,10 @@ struct Node { value: V, } ``` + ## 实现二叉树的初始化与二叉查找树的插入 由于二叉查找树要求键可排序,我们要求K实现PartialOrd + ```rust trait BinaryTree { fn pre_order(&self); @@ -68,6 +71,7 @@ impl BinarySearchTree for Node{ } } ``` + ## 二叉树的遍历 - 先序遍历:首先访问根,再先序遍历左(右)子树,最后先序遍历右(左)子树。 @@ -75,6 +79,7 @@ impl BinarySearchTree for Node{ - 后序遍历:首先后序遍历左(右)子树,再后序遍历右(左)子树,最后访问根。 下面是代码实现: + ```rust impl BinaryTree for Node { fn pre_order(&self) { @@ -108,7 +113,9 @@ impl BinaryTree for Node { } } ``` + ## 测试代码 + ```rust type BST = Node; diff --git a/data-structure/graph.md b/data-structure/graph.md index 3c73bd1..21a1d61 100644 --- a/data-structure/graph.md +++ b/data-structure/graph.md @@ -16,6 +16,7 @@ 当图带权值时,则可以直接在二维数值中存放权值,A(i,j) = null 表示不存在边 (Vi,Vj)。 下面看看我们使用邻接矩阵实现的图结构: + ```rust #[derive(Debug)] struct Node { diff --git a/data-structure/linked_list.md b/data-structure/linked_list.md index fac34cb..bafb655 100644 --- a/data-structure/linked_list.md +++ b/data-structure/linked_list.md @@ -21,6 +21,7 @@ enum List { ``` ## 实现链表的方法 + ```rust impl List { // 创建一个空链表 @@ -66,6 +67,7 @@ impl List { ``` ## 代码测试 + ```rust fn main() { let mut list = List::new(); diff --git a/data-structure/priority_queue.md b/data-structure/priority_queue.md index 18d301c..97e2872 100644 --- a/data-structure/priority_queue.md +++ b/data-structure/priority_queue.md @@ -4,23 +4,28 @@ 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (largest-in,first-out)的行为特征。 >优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有: -1) 查找; -2) 插入一个新元素; -3) 删除。 + +1. 查找; +2. 插入一个新元素; +3. 删除。 + 在最小优先队列(min priority queue)中,查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素。优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行。 ## 优先队列的实现: 首先定义 PriorityQueue 结构体 + ```rust #[derive(Debug)] struct PriorityQueue where T: PartialOrd + Clone { pq: Vec } ``` + 第二行的`where T: PartialOrd + Clone`指的是 PriorityQueue 存储的泛型 T 是满足 `PartialOrd` 和 `Clone` trait 约束的,意味着泛型 T 是可排序和克隆的。 后面是一些基本的方法实现,比较简单,就直接看代码吧。这个优先队列是基于Vec实现的,有O(1)的插入和O(n)的最大/最小值出列。 + ```rust impl PriorityQueue where T: PartialOrd + Clone { fn new() -> PriorityQueue { @@ -86,6 +91,7 @@ impl PriorityQueue where T: PartialOrd + Clone { ``` ## 测试代码: + ```rust fn test_keep_min() { let mut pq = PriorityQueue::new(); diff --git a/data-structure/stack.md b/data-structure/stack.md index a7c83e2..41c8d2a 100644 --- a/data-structure/stack.md +++ b/data-structure/stack.md @@ -24,6 +24,7 @@ struct Stack { top: Option>>, } ``` + 让我们一步步来分析 - 第一行的`#[derive(Debug)]`是为了让`Stack`结构体可以打印调试。 @@ -31,6 +32,7 @@ struct Stack { - 第三行比较复杂,在定义`StackNode`的时候介绍 ## 定义组成栈结构的栈点`StackNode` + ```rust #[derive(Clone,Debug)] struct StackNode { @@ -38,6 +40,7 @@ struct StackNode { next: Option>>, } ``` + 在这段代码的第三行, 我们定义了一个`val`保存`StackNode`的值。 >现在我们重点来看看第四行: @@ -56,6 +59,7 @@ struct StackNode { > `Option`是 Rust 里面的一个抽象类型,定义如下: > + ```rust pub enum Option { None, @@ -110,6 +114,7 @@ impl Stack { - `pop( )`的功能是取出栈顶的元素,如果栈顶为 None 则返回 None。 ## 完整代码(包含简单的测试) + ```rust #[derive(Debug)] struct Stack { diff --git a/editors/before.md b/editors/before.md index feeacbf..dd176ff 100644 --- a/editors/before.md +++ b/editors/before.md @@ -17,6 +17,7 @@ racer是一个由rust的爱好者提供的rust自动补全和语法分析工具 ### cargo自动安装 在rust 1.5版本以后,其安装包自带的cargo工具已经支持了cargo install命令,这个命令可以帮助我们通过简单的方式获取到`racer`的最新版。 + 在linux和unix系统中,你可以通过 ```bash @@ -65,13 +66,17 @@ export RUST_SRC_PATH=$RUST_SRC_HOME/src 请重新打开终端,并进入到关闭之前的路径。 执行如下代码: linux: + ``` ./target/release/racer complete std::io::B ``` + windows: + ``` target\release\racer complete std::io::B ``` + 你将会看到racer的提示,这表示racer已经执行完成了。 diff --git a/editors/emacs.md b/editors/emacs.md index e8072eb..01aa37d 100644 --- a/editors/emacs.md +++ b/editors/emacs.md @@ -10,7 +10,7 @@ Windows的Emacs用户仅作参考。 Emacs 的 rust-mode 提供了语法高亮显示和 elisp 函数,可以围绕 Rust 函数定义移动光标。有几个插件提供了附加的功能,如自动补全和动态语法检查。 -![](../image/editor-emacs-base.png) +![](../images/editor-emacs-base.png) ## 安装插件 @@ -84,7 +84,7 @@ Emacs 的 rust-mode 提供了语法高亮显示和 elisp 函数,可以围绕 R ``` -![](../image/editor-emacs-error-checking.png) +![](../images/editor-emacs-error-checking.png) ## 配置 Racer @@ -93,7 +93,7 @@ Racer 需要 Rust 的源代码用于自动补全。 - git clone https://github.com/rust-lang/rust.git ~/.rust - 重新启动 Emacs 并打开一个 Rust 源代码文件。 -![](../image/editor-emacs-completion.png) +![](../images/editor-emacs-completion.png) ## 结论 @@ -105,7 +105,7 @@ Racer 需要 Rust 的源代码用于自动补全。 - 跳转到函数定义 - 内嵌文档 -![](../image/editor-emacs-jump.gif) +![](../images/editor-emacs-jump.gif) ## 注释 diff --git a/editors/spacemacs.md b/editors/spacemacs.md index 4799f21..9c6aac9 100644 --- a/editors/spacemacs.md +++ b/editors/spacemacs.md @@ -1,4 +1,5 @@ # Spacemacs + spacemacs,是一个给vimer的Emacs。 ## 简介 spacemacs是一个专门给那些习惯vim的操作,同时又向往emacs的扩展能力的人。它非常适合我这种折腾过vim,配置过emacs的人,但同时也欢迎任何没有基础的新人使用。简单来说,它是一个开箱即用的Emacs!这对一个比很多人年龄都大的软件来说是一件极其不容易的事情。 @@ -11,23 +12,27 @@ spacemacs是一个专门给那些习惯vim的操作,同时又向往emacs的扩 在*nix系统中,都不一定会默认安装了Emacs,就算安装了,也不一定是最新的版本。在这里,我强烈建议各位卸载掉系统自带的Emacs,因为你不知道系统给你安装的是个什么奇怪的存在,最遭心的,我碰见过只提供阉割版Emacs的linux发行版。 建议各位自己去emacs项目主页下载Emacs-24.5(本书写作时的最新版)极其以上版本,然后下载下来源码。至于Emacs的安装也非常简单,linux平台老三步。 + ```bash ./configure make sudo make install ``` + 什么?你没有make?没有GCC?缺少依赖? 请安装它们…… ### Spacemacs安装 前面说了,Spacemacs就是个Emacs的配置文件库,因此我们可以通过非常简单的方式安装它: + ```bash git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d mv ~/.emacs ~/_emacs.backup cd ~/.emacs.d echo $(git describe --tags $(git rev-list --tags --max-count=1)) | xargs git checkout ``` + 其中,后三行是笔者加的,这里必须要吐槽一下的是,Spacemacs的master分支实际上是极其落后而且有错误的!其目前的release都是从develop分支上打的tag。 因此,一!定!不!要!用!主!分!支! @@ -49,6 +54,7 @@ echo $(git describe --tags $(git rev-list --tags --max-count=1)) | xargs git che Spacemacs文档中提供了一份标准的spacemacs[配置文件](https://github.com/syl20bnr/spacemacs/blob/master/core/templates/.spacemacs.template),你可以将其加入到你自己的`~/.spacemacs`文件中。 这里,我们需要修改的是其关于自定义插件的部分: + ```lisp (defun dotspacemacs/layers () "Configuration Layers declaration. diff --git a/editors/sublime.md b/editors/sublime.md index d5d9f72..9bdb773 100644 --- a/editors/sublime.md +++ b/editors/sublime.md @@ -44,12 +44,10 @@ import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc5 此时安装尚未完成,我们需要将本地的 racer.exe配置进RustAutoComplete插件中。打开编辑器顶端的Preferences选项卡,依次 Preferences->Package Settings->RustAutoComplete->Settings-User 来打开 RustAutoComplete 的配置文件,在文件中配置以下信息并保存。 ```shell - { "racer": "E:/soft/racer-master/target/release/racer.exe", "search_paths": [ "E:/soft/rustc-1.7.0/src" ] } - ``` 其中racer是编译后的racer.exe程序的绝对路径。search_paths是rust源码文件下src目录的绝对路径。 diff --git a/editors/vim.md b/editors/vim.md index 0eeca9e..97cbc86 100644 --- a/editors/vim.md +++ b/editors/vim.md @@ -6,7 +6,7 @@ 应邀而加 -![此处应该有截图](../image/editor-vim-wayslog.png) +![此处应该有截图](../images/editor-vim-wayslog.png) ## 使用vundle @@ -14,6 +14,7 @@ vundle是vim的一个插件管理工具,基本上算是本类当中最为易 首先我们需要安装它 ### linux or OS X + ```bash mkdir -p ~/.vim/bundle/ git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim @@ -81,6 +82,7 @@ let $RUST_SRC_PATH="/src/" YouCompleteMe 是 vim 下的智能补全插件, 支持 C-family, Python, Rust 等的语法补全, 整合了多种插件, 功能强大. Linux 各发行版的官方源里基本都有软件包, 可直接安装. 如果有需要进行编译安装的话, 可参考[官方教程](https://github.com/Valloric/YouCompleteMe#installation) 让 YCM 支持 Rust 需要在安装 YCM 过程中执行 ./install.py 时加上 --racer-completer, 并在 .vimrc 中添加如下设置 + ``` let g:ycm_rust_src_path="/src/" "" 一些方便的快捷键 @@ -100,7 +102,7 @@ inoremap ; 总体来看支持度并不高。 -![此处应该有第二张截图](../image/editor-vim-welldone.png) +![此处应该有第二张截图](../images/editor-vim-welldone.png) ### 额外的 Q1. 颜色好挫 diff --git a/editors/visualstudio.md b/editors/visualstudio.md index 5912ca4..4a023b5 100644 --- a/editors/visualstudio.md +++ b/editors/visualstudio.md @@ -1,4 +1,5 @@ # Visual Studio + 本文是使用VisualRust和VS GDB Debugger / VisualGDB 完成在VisualStudio中,编辑和调试Rust程序。 ## 安装Rust, Cargo @@ -6,17 +7,20 @@ 首先需要下载Rust, 下载地址https://www.rust-lang.org/downloads.html 这里一定要下windows GNU ABI的版本, 因为我们要用GDB来调试. -![](../image/editor-visualstudio-download.png) + +![](../images/editor-visualstudio-download.png) 另外,机器上也需要安装Visual Studio2013或2015。 安装完Rust,打开命令行,执行 cargo install racer -![](../image/editor-visualstudio-racer.png) + +![](../images/editor-visualstudio-racer.png) Racer是用来做Rust自动完成的,会在VisualRust使用。这里我们使用rust编译的racer, 并不用VisualRust里自带的racer,因为它太旧了. 另外需要下载Rust源代码,设置 RUST_SRC_PATH为Rust源代码src的目录 -![](../image/editor-visualstudio-racersc.png) + +![](../images/editor-visualstudio-racersc.png) ## 安装VisualRust和VS GDB Debugger @@ -30,11 +34,11 @@ http://www.visualgdb.com/ ## 编译Rust项目 新建Rust项目 -![](../image/editor-visualstudio-newproject.png) +![](../images/editor-visualstudio-newproject.png) 在tool, option里设置racer和rust_src_path -![](../image/editor-visualstudio-settings.png) +![](../images/editor-visualstudio-settings.png) 这时候就可以在写代码的时候就可以自动提示了。像下面这样 -![](../image/editor-visualstudio-autocomplete.png) +![](../images/editor-visualstudio-autocomplete.png) ## 用VS GDB Debugger调试Rust项目 @@ -42,33 +46,33 @@ ok,愉快的开始你的Rust之旅吧。下面开始使用VS GDB Debugger调试R 在解决方案中,添加GDB调试项目 -![](../image/editor-visualstudio-GDBproject.png) +![](../images/editor-visualstudio-GDBproject.png) 设置需要调试的程序所在的目录和文件名 -![](../image/editor-visualstudio-GDBproject-settings.png) +![](../images/editor-visualstudio-GDBproject-settings.png) 设置需要调试的程序的编译命令,此处用rustc,也可以使用cargo编译 -![](../image/editor-visualstudio-GDBproject-settings2.png) +![](../images/editor-visualstudio-GDBproject-settings2.png) 将需要调试的程序的源代码添加到项目目录下 -![](../image/editor-visualstudio-add-files.png) +![](../images/editor-visualstudio-add-files.png) 打开源代码文件并设置断点信息,将项目设置为启动项目并选择Local GDB即可开始调试 -![](../image/editor-visualstudio-set-breakpoints.png) +![](../images/editor-visualstudio-set-breakpoints.png) -![](../image/editor-visualstudio-debugging2.png) +![](../images/editor-visualstudio-debugging2.png) ## 用VisualGDB调试Rust项目 Build完Rust程序,点击debug, 选择quick debug with gdb -![](../image/editor-visualstudio-quickdebug.png) +![](../images/editor-visualstudio-quickdebug.png) 然后在里面选择MingW和exe的路径 -![](../image/editor-visualstudio-setdebugger.png) +![](../images/editor-visualstudio-setdebugger.png) 点击Debug,开始你的调试生活吧 -![](../image/editor-visualstudio-debugging.png) +![](../images/editor-visualstudio-debugging.png) diff --git a/error-handling/option-result.md b/error-handling/option-result.md index 2eb7278..d8e7210 100644 --- a/error-handling/option-result.md +++ b/error-handling/option-result.md @@ -32,7 +32,9 @@ enum Option { Some(T), } ``` + **Option** 是Rust的系统类型,用来表示值不存在的可能,这在编程中是一个好的实践,它强制**Rust**检测和处理值不存在的情况。例如: + ```rust fn find(haystack: &str, needle: char) -> Option { for (offset, c) in haystack.char_indices() { @@ -43,6 +45,7 @@ fn find(haystack: &str, needle: char) -> Option { None } ``` + `find`在字符串`haystack`中查找`needle`字符,事实上结果会出现两种可能,有(`Some(usize)`)或无(`None`)。 ```rust @@ -54,9 +57,11 @@ fn main() { } } ``` + **Rust** 使用模式匹配来处理返回值,调用者必须处理结果为`None`的情况。这往往是一个好的编程习惯,可以减少潜在的bug。**Option** 包含一些方法来简化模式匹配,毕竟过多的`match`会使代码变得臃肿,这也是滋生bug的原因之一。 #### unwrap + ```rust impl Option { fn unwrap(self) -> T { @@ -68,6 +73,7 @@ impl Option { } } ``` + `unwrap`当遇到`None`值时会panic,如前面所说这不是一个好的工程实践。不过有些时候却非常有用: * **在例子和简单快速的编码中** 有的时候你只是需要一个小例子或者一个简单的小程序,输入输出已经确定,你根本没必要花太多时间考虑错误处理,使用`unwrap`变得非常合适。 @@ -77,6 +83,7 @@ impl Option { #### map 假如我们要在一个字符串中找到文件的扩展名,比如`foo.rs`中的`rs`, 我们可以这样: + ```rust fn extension_explicit(file_name: &str) -> Option<&str> { match find(file_name, '.') { @@ -92,7 +99,9 @@ fn main() { } } ``` + 我们可以使用`map`简化: + ```rust // map是标准库中的方法 fn map(option: Option, f: F) -> Option where F: FnOnce(T) -> A { @@ -106,6 +115,7 @@ fn extension(file_name: &str) -> Option<&str> { find(file_name, '.').map(|i| &file_name[i+1..]) } ``` + `map`如果有值`Some(T)`会执行`f`,反之直接返回`None`。 #### unwrap_or @@ -127,6 +137,7 @@ fn main() { ``` #### and_then + ```rust fn and_then(option: Option, f: F) -> Option where F: FnOnce(T) -> Option { @@ -136,6 +147,7 @@ fn and_then(option: Option, f: F) -> Option } } ``` + 看起来`and_then`和`map`差不多,不过`map`只是把值为`Some(t)`重新映射了一遍,`and_then`则会返回另一个`Option`。如果我们在一个文件路径中找到它的扩展名,这时候就会变得尤为重要: ```rust @@ -150,18 +162,23 @@ fn file_path_ext(file_path: &str) -> Option<&str> { ``` ### Result + ```rust enum Result { Ok(T), Err(E), } ``` + `Result`是`Option`的更通用的版本,比起`Option`结果为`None`它解释了结果错误的原因,所以: + ```rust type Option = Result; ``` + 这样的别名是一样的(`()`标示空元组,它既是`()`类型也可以是`()`值) #### unwrap + ```rust impl Result { fn unwrap(self) -> T { @@ -173,9 +190,11 @@ impl Result { } } ``` + 没错和`Option`一样,事实上它们拥有很多类似的方法,不同的是,`Result`包括了错误的详细描述,这对于调试人员来说,这是友好的。 #### Result我们从例子开始 + ```rust fn double_number(number_str: &str) -> i32 { 2 * number_str.parse::().unwrap() @@ -186,6 +205,7 @@ fn main() { assert_eq!(n, 20); } ``` + `double_number`从一个字符串中解析出一个`i32`的数字并`*2`,`main`中调用看起来没什么问题,但是如果把`"10"`换成其他解析不了的字符串程序便会panic ```rust @@ -194,6 +214,7 @@ impl str { } ``` + `parse`返回一个`Result`,但让我们也可以返回一个`Option`,毕竟一个字符串要么能解析成一个数字要么不能,但是`Result`给我们提供了更多的信息(要么是一个空字符串,一个无效的数位,太大或太小),这对于使用者是友好的。当你面对一个Option和Result之间的选择时。如果你可以提供详细的错误信息,那么大概你也应该提供。 这里需要理解一下`FromStr`这个**trait**: @@ -211,9 +232,11 @@ impl FromStr for i32 { } } ``` + `number_str.parse::()`事实上调用的是`i32`的`FromStr`实现。 我们需要改写这个例子: + ```rust use std::num::ParseIntError; @@ -228,6 +251,7 @@ fn main() { } } ``` + 不仅仅是`map`,`Result`同样包含了`unwrap_or`和`and_then`。也有一些特有的针对错误类型的方法`map_err`和`or_else`。 #### Result别名 @@ -246,6 +270,7 @@ fn double_number(number_str: &str) -> Result { ### 组合Option和Result `Option`的方法`ok_or`: + ```rust fn ok_or(option: Option, err: E) -> Result { match option { @@ -254,7 +279,9 @@ fn ok_or(option: Option, err: E) -> Result { } } ``` + 可以在值为`None`的时候返回一个`Result::Err(E)`,值为`Some(T)`的时候返回`Ok(T)`,利用它我们可以组合`Option`和`Result`: + ```rust use std::env; @@ -272,9 +299,11 @@ fn main() { } } ``` + `double_arg`将传入的命令行参数转化为数字并翻倍,`ok_or`将`Option`类型转换成`Result`,`map_err`当值为`Err(E)`时调用作为参数的函数处理错误 #### 复杂的例子 + ```rust use std::fs::File; use std::io::Read; @@ -303,8 +332,10 @@ fn main() { } } ``` + `file_double`从文件中读取内容并将其转化成`i32`类型再翻倍。 这个例子看起来已经很复杂了,它使用了多个组合方法,我们可以使用传统的`match`和`if let`来改写它: + ```rust use std::fs::File; use std::io::Read; @@ -333,9 +364,11 @@ fn main() { } } ``` + 这两种方法个人认为都是可以的,依具体情况来取舍。 ### try! + ```rust macro_rules! try { ($e:expr) => (match $e { @@ -345,8 +378,10 @@ macro_rules! try { } ``` + `try!`事实上就是`match Result`的封装,当遇到`Err(E)`时会提早返回, `::std::convert::From::from(err)`可以将不同的错误类型返回成最终需要的错误类型,因为所有的错误都能通过`From`转化成`Box`,所以下面的代码是正确的: + ```rust use std::error::Error; use std::fs::File; @@ -362,7 +397,9 @@ fn file_double>(file_path: P) -> Result> { } ``` + #### 组合自定义错误类型 + ```rust use std::fs::File; use std::io::{self, Read}; @@ -398,6 +435,7 @@ fn file_double_verbose>(file_path: P) -> Result { Ok(2 * n) } ``` + `CliError`分别为`io::Error`和`num::ParseIntError`实现了`From`这个trait,所有调用`try!`的时候这两种错误类型都能转化成`CliError`。 ### 总结 diff --git a/ffi/calling-ffi-function.md b/ffi/calling-ffi-function.md index fe3df5e..c8bda1c 100644 --- a/ffi/calling-ffi-function.md +++ b/ffi/calling-ffi-function.md @@ -17,6 +17,7 @@ libc = "0.2.9" ``` 在你的rs文件中引入库: + ```rust extern crate libc ``` @@ -48,6 +49,7 @@ extern { 声明完成后就可以进行调用了。 由于此函数来自外部的c库,所以rust并不能保证该函数的安全性。因此,调用任何一个`ffi`函数需要一个`unsafe`块。 + ```rust let result: size_t = unsafe { your_func(1 as c_int, Box::into_raw(Box::new(3)) as *mut c_void) @@ -58,6 +60,7 @@ let result: size_t = unsafe { 作为一个库作者,对外暴露不安全接口是一种非常不合格的做法。在做c库的`rust binding`时,我们做的最多的将是将不安全的c接口封装成一个安全接口。 通常做法是:在一个叫`ffi.rs`之类的文件中写上所有的`extern块`用以声明ffi函数。在一个叫`wrapper.rs`之类的文件中进行包装: + ```rust // ffi.rs #[link(name = "yourlib")] @@ -65,12 +68,14 @@ extern { fn your_func(arg1: c_int, arg2: *mut c_void) -> size_t; } ``` + ```rust // wrapper.rs fn your_func_wrapper(arg1: i32, arg2: &mut i32) -> isize { unsafe { your_func(1 as c_int, Box::into_raw(Box::new(3)) as *mut c_void) } as isize } ``` + 对外暴露(pub use) `your_func_wrapper`函数即可。 ## 数据结构对应 @@ -88,6 +93,7 @@ struct RustObject { // other members } ``` + 此外,如果使用`#[repr(C, packed)]`将不为此结构体填充空位用以对齐。 ### Union @@ -118,7 +124,9 @@ fn main() { } } ``` + 对应c库代码: + ```c typedef void (*rust_callback)(int32_t); @@ -134,6 +142,7 @@ rust为了应对不同的情况,有很多种字符串类型。其中`CStr`和` #### CStr 对于产生于c的字符串(如在c程序中使用`malloc`产生),rust使用`CStr`来表示,和`str`类型对应,表明我们并不拥有这个字符串。 + ```rust use std::ffi::CStr; use libc::c_char; @@ -150,6 +159,7 @@ fn get_string() -> String { } } ``` + 在这里`get_string`使用`CStr::from_ptr`从c的`char*`获取一个字符串,并且转化成了一个String. * 注意to_string_lossy()的使用:因为在rust中一切字符都是采用utf8表示的而c不是, @@ -159,6 +169,7 @@ fn get_string() -> String { #### CString 和`CStr`表示从c中来,rust不拥有归属权的字符串相反,`CString`表示由rust分配,用以传给c程序的字符串。 + ```rust use std::ffi::CString; use std::os::raw::c_char; @@ -172,6 +183,7 @@ unsafe { my_printer(c_to_print.as_ptr()); // 使用 as_ptr 将CString转化成char指针传给c函数 } ``` + 注意c字符串中并不能包含`\0`字节(因为`\0`用来表示c字符串的结束符),因此`CString::new`将返回一个`Result`, 如果输入有`\0`的话则为`Error(NulError)`。 @@ -181,6 +193,7 @@ C库存在一种常见的情况:库作者并不想让使用者知道一个数 比较典型的是`ncurse`库中的`WINDOW`类型。 当参数是`void*`时,在rust中可以和c一样,使用对应类型`*mut libc::c_void`进行操作。如果参数为不透明结构体,rust中可以使用空白`enum`进行代替: + ```rust enum OpaqueStruct {} @@ -188,7 +201,9 @@ extern "C" { pub fn foo(arg: *mut OpaqueStruct); } ``` + C代码: + ```c struct OpaqueStruct; void foo(struct OpaqueStruct *arg); diff --git a/ffi/compiling-rust-to-lib.md b/ffi/compiling-rust-to-lib.md index b7a640c..69389c1 100644 --- a/ffi/compiling-rust-to-lib.md +++ b/ffi/compiling-rust-to-lib.md @@ -14,12 +14,15 @@ #[no_mangle] extern "C" fn test() {} ``` + 在nm中观察到为 + ``` ... 00000000001a7820 T test ... ``` + 至此,`test`函数将能够被正常的由`cffi`调用。 ## 指定`crate`类型 `rustc`默认编译产生`rust`自用的`rlib`格式库,要让`rustc`产生动态链接库或者静态链接库,需要显式指定。 @@ -33,6 +36,7 @@ extern "C" fn test() {} ## 小技巧: `Any` 由于在跨越`ffi`过程中,`rust`类型信息会丢失,比如当用`rust`提供一个`OpaqueStruct`给别的语言时: + ```rust use std::mem::transmute; @@ -68,6 +72,7 @@ extern "C" fn push_foo_element_c(foo: *mut c_void){ 因此在`ffi`调用时往往会丧失掉`rust`类型系统带来的方便和安全。在这里提供一个小技巧:使用`Box>`来包装你的类型。 `rust`的`Any`类型为`rust`带来了运行时反射的能力,使用`Any`跨越`ffi`边界将极大提高程序安全性。 + ```rust use std::any::Any; @@ -101,4 +106,5 @@ extern "C" fn push_foo_element_c(foo: *mut c_void){ } } ``` + 这样一来,就非常不容易出错了。 diff --git a/ffi/preface.md b/ffi/preface.md index 15867c7..6714ac0 100644 --- a/ffi/preface.md +++ b/ffi/preface.md @@ -1,5 +1,5 @@ -FFI ------------------ +# FFI + 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 调用。前者需要至少两个独立的进程才能实现,而后者直接将其它语言的接口内嵌到本语言中,所以调用效率比前者高。 diff --git a/flow/repetition.md b/flow/repetition.md index d3503e2..cf4424b 100644 --- a/flow/repetition.md +++ b/flow/repetition.md @@ -10,20 +10,25 @@ ## for for 语句用于遍历一个迭代器。 + ```rust for var in iterator { code } ``` + Rust 迭代器返回一系列的元素,每个元素是循环中的一次重复。然后它的值与 var 绑定,它在循环体中有效。每当循环体执行完后,我们从迭代器中取出下一个值,然后我们再重复一遍。当迭代器中不再有值时,for 循环结束。 比如: + ```rust for x in 0..10 { println!("{}", x); // x: i32 } ``` + 输出 + ``` 0 1 @@ -62,6 +67,7 @@ for (i,j) in (5..10).enumerate() { println!("i = {} and j = {}", i, j); } ``` + 输出: ``` @@ -71,6 +77,7 @@ i = 2 and j = 7 i = 3 and j = 8 i = 4 and j = 9 ``` + 再比如: ```rust @@ -82,7 +89,9 @@ for (linenumber, line) in lines.enumerate() { println!("{}: {}", linenumber, line); } ``` + 输出: + ``` 0: Content of line one 1: Content of line two @@ -175,6 +184,7 @@ while !done { } } ``` + 可以优化成: ```rust @@ -188,6 +198,7 @@ loop { if x % 5 == 0 { break; } } ``` + 这样感觉更直观一点。 下面这个例子演示 continue 的用法: @@ -199,6 +210,7 @@ for x in 0..10 { println!("{}", x); } ``` + 它的作用是打印出 `0~9` 的奇数。结果如下: ``` diff --git a/function/arguement.md b/function/arguement.md index f1c3e31..f18ea1f 100644 --- a/function/arguement.md +++ b/function/arguement.md @@ -2,6 +2,7 @@ ## 参数声明 rust的函数参数声明和一般的变量声明相仿,也是参数名后加冒号,冒号后跟参数类型,不过不需要`let`关键字。需要注意的是,普通变量声明(let语句)是可以省略变量类型的,而函数参数的声明则不能省略参数类型。 来看一个简单例子: + ```rust fn main() { say_hi("ruster"); @@ -11,10 +12,12 @@ fn say_hi(name: &str) { println!("Hi, {}", name); } ``` + 上例中,`say_hi`函数拥有一个参数,名为`name`,类型为`&str`。 ## 将函数作为参数 在rust中,函数是一等公民(可以储存在变量/数据结构中,可以作为参数传入函数,可以作为返回值),所以rust的函数参数不仅可以是一般的类型,也可以是函数。如: + ```rust fn main() { let xm = "xiaoming"; @@ -35,10 +38,12 @@ fn say_what(name: &str, func: fn(&str)) { func(name) } ``` + 上例中,`hi`函数和`hello`函数都是只有一个`&str`类型的参数且没有返回值。而`say_what`函数则有两个参数,一个是`&str`类型,另一个则是函数类型(function type),它是只有一个`&str`类型参数且没有返回值的函数类型。 ## 模式匹配 支持模式匹配,让rust平添了许多的灵活性,用起来也是十分的舒爽。模式匹配不仅可以用在变量声明(let语句)中,也可以用在函数参数声明中,如: + ```rust fn main() { let xm = ("xiaoming", 54); @@ -63,4 +68,5 @@ fn print_name((name,_): (&str, i32)) { println!("I am {}", name); } ``` + 上例是一个元组(Tuple)匹配的例子,当然也可以是其他可在let语句中使用的类型。参数的模式匹配跟let语句的匹配一样,也可以使用下划线来表示丢弃一个值。 diff --git a/function/higher_order_function.md b/function/higher_order_function.md index 97aa61e..45932b0 100644 --- a/function/higher_order_function.md +++ b/function/higher_order_function.md @@ -6,6 +6,7 @@ > 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. 来看一个简单例子: + ```rust fn inc(n: i32) -> i32 {//函数定义 n + 1 @@ -18,8 +19,10 @@ fn main() { println!("3 + 1 = {}", func(3)); } ``` + 上例首先使用`fn`定义了`inc`函数,它有一个`i32`类型参数,返回`i32`类型的值。然后再用`fn`定义了一个函数类型,这个函数类型有i32类型的参数和i32类型的返回值,并用`type`关键字定义了它的别名`IncType`。在`main`函数中定义了一个变量`func`,其类型就为`IncType`,并赋值为`inc`,然后在`pirntln`宏中调用:`func(3)`。可以看到,`inc`函数的类型其实就是`IncType`。 这里有一个问题,我们将`inc`赋值给了`func`,而不是`&inc`,这样是将`inc`函数的拥有权转给了`func`吗,赋值后还可以以`inc()`形式调用`inc`函数吗?先来看一个例子: + ```rust fn main() { let func: IncType = inc; @@ -33,15 +36,19 @@ fn inc(n: i32) -> i32 { n + 1 } ``` + 我们将上例保存在rs源文件中,再用rustc编译,发现并没有报错,并且运行也得到我们想要的结果: + ``` 3 + 1 = 4 3 + 1 = 4 ``` + 这说明,赋值时,`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`关键字声明。 ## 函数作为参数 函数作为参数,其声明与普通参数一样。看下例: + ```rust fn main() { println!("3 + 1 = {}", process(3, inc)); @@ -60,12 +67,16 @@ fn process(n: i32, func: fn(i32) -> i32) -> i32 { func(n) } ``` + 例子中,`process`就是一个高阶函数,它有两个参数,一个类型为`i32`的`n`,另一个类型为`fn(i32)->i32`的函数`func`,返回一个`i32`类型的参数;它在函数体内以`n`作为参数调用`func`函数,返回`func`函数的返回值。运行可以得到以下结果: + ``` 3 + 1 = 4 3 - 1 = 2 ``` + 不过,这不是函数作为参数的唯一声明方法,使用泛型函数配合特质(`trait`)也是可以的,因为rust的函数都会实现一个`trait`:`FnOnce`、`Fn`或`FnMut`。将上例中的`process`函数定义换成以下形式是等价的: + ```rust fn process(n: i32, func: F) -> i32 where F: Fn(i32) -> i32 { @@ -75,6 +86,7 @@ fn process(n: i32, func: F) -> i32 ## 函数作为返回值 函数作为返回值,其声明与普通函数的返回值类型声明一样。看例子: + ```rust fn main() { let a = [1,2,3,4,5,6,7]; @@ -99,8 +111,10 @@ fn get_func(n: i32) -> fn(i32) -> i32 { } } ``` + 例子中的高阶函数为`get_func`,它接收一个i32类型的参数,返回一个类型为`fn(i32) -> i32`的函数,若传入的参数为偶数,返回`inc`,否则返回`dec`。这里需要注意的是,`inc`函数和`dec`函数都定义在`get_func`内。在函数内定义函数在很多其他语言中是不支持的,不过rust支持,这也是rust灵活和强大的一个体现。不过,在函数中定义的函数,不能包含函数中(环境中)的变量,若要包含,应该闭包(详看13章 闭包)。 所以下例: + ```rust fn main() { let f = get_func(); @@ -115,4 +129,6 @@ fn get_func() -> fn(i32)->i32 { inc } ``` - 使用rustc编译,会出现如下错误:![error](../image/high-order-function.png) + + 使用rustc编译,会出现如下错误: + ![error](../images/high-order-function.png) diff --git a/function/overview.md b/function/overview.md index fb0dec2..718f5d4 100644 --- a/function/overview.md +++ b/function/overview.md @@ -1,10 +1,12 @@ # 函数 尽管rust是一门多范式的编程语言,但rust的编程风格是更偏向于函数式的,函数在rust中是“一等公民”——first-class type。这意味着,函数是可以作为数据在程序中进行传递,如:作为函数的参数。跟C、C++一样,rust程序也有一个唯一的程序入口-main函数。rust的main函数形式如下: + ```rust fn main() { //statements } ``` + rust使用 `fn` 关键字来声明和定义函数,`fn` 关键字隔一个空格后跟函数名,函数名后跟着一个括号,函数参数定义在括号内。rust使用`snake_case`风格来命名函数,即所有字母小写并使用下划线类分隔单词,如:`foo_bar`。如果函数有返回值,则在括号后面加上箭头 __->__ ,在箭头后加上返回值的类型。 这一章我们将学习以下与函数相关的知识: diff --git a/function/return_value.md b/function/return_value.md index a37aaf1..bf511dc 100644 --- a/function/return_value.md +++ b/function/return_value.md @@ -1,22 +1,27 @@ # 函数返回值 在rust中,任何函数都有返回类型,当函数返回时,会返回一个该类型的值。我们先来看看main函数: + ```rust fn main() { //statements } ``` + 之前有说过,函数的返回值类型是在参数列表后,加上箭头和类型来指定的。不过,一般我们看到的main函数的定义并没有这么做。这是因为main函数的返回值是`()`,在rust中,当一个函数返回`()`时,可以省略。main函数的完整形式如下: + ```rust fn main() -> () { //statements } ``` + main函数的返回值类型是`()`,它是一个特殊的元组——没有元素的元组,称为`unit`,它表示一个函数没有任何信息需要返回。在Rust Reference的[8.1.3 Tuple types](https://doc.rust-lang.org/reference.html#tuple-types)中是的描述如下: > For historical reasons and convenience, the tuple type with no elements (__()__) is often called ‘unit’ or ‘the unit type’. `()`类型,其实类似于C/C++、Java、C#中的`void`类型。 下面来看一个有返回值的例子: + ```rust fn main() { let a = 3; @@ -27,10 +32,12 @@ n + 1 } ``` + 上面的例子中,函数`inc`有一个`i32`类型的参数和返回值,作用是将参数加1返回。需要注意的是`inc`函数中只有`n+1`一个表达式,并没有像C/C++或Java、C#等语言有显式地`return`语句类返回一个值。这是因为,与其他基于语句的语言(如C语言)不同,rust是基于表达式的语言,函数中最后一个表达式的值,默认作为返回值。当然,rust中也有语句,关于rust的语句和表达式,请看[下一节](statement_expression.md)。 ## return关键字 rust也有`return`关键字,不过一般用于提前返回。来看一个简单地例子: + ```rust fn main() { let a = [1,3,2,5,9,8]; @@ -47,10 +54,12 @@ fn find(n: i32, a: &[i32]) -> bool { false } ``` + 上例中,`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)中的相关内容。 ## 返回多个值 rust的函数不支持多返回值,但是我们可以利用元组来返回多个值,配合rust的模式匹配,使用起来十分灵活。先看例子: + ```rust fn main() { let (p2,p3) = pow_2_3(789); @@ -62,10 +71,12 @@ fn pow_2_3(n: i32) -> (i32, i32) { (n*n, n*n*n) } ``` + 可以看到,上例中,`pow_2_3`函数接收一个`i32`类型的值,返回其二次方和三次方的值,这两个值包装在一个元组中返回。在`main`函数中,`let`语句就可以使用模式匹配将函数返回的元组进行解构,将这两个返回值分别赋给`p2`和`p3`,从而可以得到`789`二次方的值和三次方的值。 ## 发散函数 发散函数(diverging function)是rust中的一个特性。发散函数不返回,它使用感叹号`!`作为返回类型表示: + ```rust fn main() { println!("hello"); @@ -77,7 +88,8 @@ fn diverging() -> ! { panic!("This function will never return"); } ``` - 由于发散函数不会返回,所以就算其后再有其他语句也是不会执行的。倘若其后还有其他语句,会出现如下编译警告:![error](../image/function-return-value.png)。当然了,我们要知道的是不发散的函数也是可以不返回的,比如无限循环之类的。 + + 由于发散函数不会返回,所以就算其后再有其他语句也是不会执行的。倘若其后还有其他语句,会出现如下编译警告:![error](../images/function-return-value.png)。当然了,我们要知道的是不发散的函数也是可以不返回的,比如无限循环之类的。 发散函数一般都以`panic!`宏调用或其他调用其他发散函数结束,所以,调用发散函数会导致当前线程崩溃。[Rust Reference 6.1.3.2 Diverging functions][ref]中的描述如下: > 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. diff --git a/function/statement_expression.md b/function/statement_expression.md index 8b3f157..98d3cd7 100644 --- a/function/statement_expression.md +++ b/function/statement_expression.md @@ -4,16 +4,20 @@ ## 声明语句 rust的声明语句可以分为两种,一种为变量声明语句,另一种为Item声明语句。 1. 变量声明语句。主要是指`let`语句,如: + ```rust let a = 8; let b: Vec = Vec::new(); let (a, c) = ("hi", false); ``` + 由于let是语句,所以不能将let语句赋给其他值。如下形式是错误的: + ```rust let b = (let a = 8); ``` - rustc编译器会给出错误信息:![error](../image/function-statement-expression.png) + + rustc编译器会给出错误信息:![error](../images/function-statement-expression.png) 2. Item声明。是指函数(function)、结构体(structure)、类型别名(type)、静态变量(static)、特质(trait)、实现(implementation)或模块(module)的声明。这些声明可以嵌套在任意块(block)中。关于Item声明,Rust Reference中的描述如下: > 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. @@ -25,6 +29,7 @@ __rust有许多种表达式:__ * 字面表达式(literal expression) + ```rust (); // unit type "hello"; // string type @@ -33,11 +38,14 @@ ``` * 元组表达式(Tuple expression): + ```rust (0.0, 4.5); ("a", 4usize, true); ``` + 通常不使用一个元素的元组,不过如果你坚持的话,rust也是允许的,不过需要在元素后加一个逗号: + ```rust (0,); // single-element tuple (0); // zero in parentheses @@ -45,13 +53,16 @@ * 结构体表达式(structure expression) 由于结构体有多种形式,所以结构体表达式也有多种形式。 + ```rust Point {x: 10.0, y: 20.0}; TuplePoint(10.0, 20.0); let u = game::User {name: "Joe", age: 35, score: 100_000}; some_fn::(Cookie); ``` + 结构体表达式一般用于构造一个结构体对象,它除了以上从零构建的形式外,还可以在另一个对象的基础上进行构建: + ```rust let base = Point3d {x: 1, y: 2, z: 3}; Point3d {y: 0, z: 10, .. base}; @@ -59,16 +70,20 @@ * 块表达式(block expression): 块表达式就是用花括号`{}`括起来的一组表达式的集合,表达式间一般以分号分隔。块表达式的值,就是最后一个表达式的值。 + ```rust let x: i32 = { println!("Hello."); 5 }; ``` + 如果以语句结尾,则块表达式的值为`()`: + ```rust let x: () = { println!("Hello."); }; ``` * 范围表达式(range expression): 可以使用范围操作符`..`来构建范围对象(variant of `std::ops::Range`): + ```rust 1..2; // std::ops::Range 3..; // std::ops::RangeFrom @@ -77,6 +92,7 @@ ``` * if表达式(if expression): + ```rust let a = 9; let b = if a%2 == 0 {"even"} else {"odd"}; diff --git a/generic/generic.md b/generic/generic.md index ec85110..de64f98 100644 --- a/generic/generic.md +++ b/generic/generic.md @@ -1,7 +1,8 @@ -泛型 -=================== +# 泛型 + 我们在编程中,通常有这样的需求,为多种类型的数据编写一个功能相同的函数,如两个数的加法,希望这个函数既支持i8、i16、 i32 ....float64等等,甚至自定义类型,在不支持泛型的编程语言中,我们通常要为每一种类型都编写一个函数,而且通常情况下函数名还必须不同,例如: + ```rust fn add_i8(a:i8, b:i8) -> i8 { a + b @@ -27,9 +28,10 @@ fn main() { 学过C++的人也许很容易理解泛型,但本教程面向的是Rust初学者,所以不会拿C++的泛型、多态和Rust进行对比,以免增加学习的复杂度和不必要的困扰,从而让Rust初学者更容易理解和接受Rust泛型。 -概念 -------------- +## 概念 + 泛型程序设计是程序设计语言的一种风格或范式。允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时(instantiate)作为参数指明这些类型(在Rust中,有的时候类型还可以被编译器推导出来)。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。Ada, Delphi, Eiffel, Java, C#, F#, Swift, and Visual Basic .NET称之为泛型(generics);ML, Scala and Haskell称之为参数多态(parametric polymorphism);C++与D称之为模板。具有广泛影响的1994年版的《Design Patterns》一书称之为参数化类型(parameterized type)。 + >提示: >以上概念摘自[《维基百科-泛型》](https://zh.wikipedia.org/wiki/%E6%B3%9B%E5%9E%8B) @@ -41,19 +43,24 @@ fn main() { 泛型,可以理解为具有某些功能共性的集合类型,如i8、i16、u8、f32等都可以支持add,甚至两个struct Point类型也可以add形成一个新的Point。 先让我们来看看标准库中常见的泛型Option,它的原型定义: + ```rust enum Option { Some(T), None, } ``` + T就是泛型参数,这里的T可以换成A-Z任何你自己喜欢的字母。不过习惯上,我们用T表示Type,用E表示Error。T在具体使用的时候才会被实例化: + ```rust let a = Some(100.111f32); ``` + 编译器会自行推导出a为Option类型,也就是说Option中的T在这里是f32类型。 当然,你也可以显式声明a的类型,但必须保证和右值的类型一样,不然编译器会报"mismatched types"类型不匹配错误。 + ```rust let a:Option = Some(100.111); //编译自动推导右值中的100.111为f32类型。 let b:Option = Some(100.111f32); @@ -65,6 +72,7 @@ let d:Option = Some(100.111f64); ### 泛型函数 至此,我们已经了解到泛型的定义和简单的使用了。 现在让我们用函数重写add操作: + ```rust use std::ops::Add; @@ -86,6 +94,7 @@ fn main() { ### 自定义类型 上面的例子,add的都是语言内置的基础数据类型,当然我们也可以为自己自定义的数据结构类型实现add操作。 + ```rust use std::ops::Add; @@ -119,6 +128,7 @@ fn main() { println!("{:?}", add(p1, p2)); } ``` + >**输出:** >101 200.33 @@ -127,6 +137,7 @@ Point { x: 3, y: 3 } 上面的例子稍微更复杂些了,只是我们增加了自定义的类型,然后让add函数依然可以在上面工作。如果对trait不熟悉,请查阅trait相关章节。 大家可能会疑问,那我们是否可以让Point也变成泛型的,这样Point的x和y也能够支持float类型或者其他类型,答案当然是可以的。 + ```rust use std::ops::Add; @@ -179,14 +190,18 @@ Point { x: 3, y: 3 } ##### 问题描述 有时候我们可能做些文本分析工作, 数据可能来源于外部或者程序内置的文本. + 请实现一个 `parse` 函数, 只接收一个 lines iterator 为参数, 并输出每行. + 要求既能输出内置的文本, 也能输出文件内容. ##### 调用方式及输出参考 + ``` let lines = "some\nlong\ntext".lines() parse(do_something_or_nothing(lines)) ``` + ``` some long @@ -200,6 +215,7 @@ use std::io::BufReader; let lines = BufReader::new(File::open("/etc/hosts").unwrap()).lines() parse(do_some_other_thing_or_nothing(lines)) ``` + ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost diff --git a/image/0.png b/images/0.png similarity index 100% rename from image/0.png rename to images/0.png diff --git a/image/editor-emacs-base.png b/images/editor-emacs-base.png similarity index 100% rename from image/editor-emacs-base.png rename to images/editor-emacs-base.png diff --git a/image/editor-emacs-completion.png b/images/editor-emacs-completion.png similarity index 100% rename from image/editor-emacs-completion.png rename to images/editor-emacs-completion.png diff --git a/image/editor-emacs-error-checking.png b/images/editor-emacs-error-checking.png similarity index 100% rename from image/editor-emacs-error-checking.png rename to images/editor-emacs-error-checking.png diff --git a/image/editor-emacs-jump.gif b/images/editor-emacs-jump.gif similarity index 100% rename from image/editor-emacs-jump.gif rename to images/editor-emacs-jump.gif diff --git a/image/editor-vim-wayslog.png b/images/editor-vim-wayslog.png similarity index 100% rename from image/editor-vim-wayslog.png rename to images/editor-vim-wayslog.png diff --git a/image/editor-vim-welldone.png b/images/editor-vim-welldone.png similarity index 100% rename from image/editor-vim-welldone.png rename to images/editor-vim-welldone.png diff --git a/image/editor-visualstudio-GDBproject-settings.png b/images/editor-visualstudio-GDBproject-settings.png similarity index 100% rename from image/editor-visualstudio-GDBproject-settings.png rename to images/editor-visualstudio-GDBproject-settings.png diff --git a/image/editor-visualstudio-GDBproject-settings2.png b/images/editor-visualstudio-GDBproject-settings2.png similarity index 100% rename from image/editor-visualstudio-GDBproject-settings2.png rename to images/editor-visualstudio-GDBproject-settings2.png diff --git a/image/editor-visualstudio-GDBproject.png b/images/editor-visualstudio-GDBproject.png similarity index 100% rename from image/editor-visualstudio-GDBproject.png rename to images/editor-visualstudio-GDBproject.png diff --git a/image/editor-visualstudio-add-files.png b/images/editor-visualstudio-add-files.png similarity index 100% rename from image/editor-visualstudio-add-files.png rename to images/editor-visualstudio-add-files.png diff --git a/image/editor-visualstudio-autocomplete.png b/images/editor-visualstudio-autocomplete.png similarity index 100% rename from image/editor-visualstudio-autocomplete.png rename to images/editor-visualstudio-autocomplete.png diff --git a/image/editor-visualstudio-debugging.png b/images/editor-visualstudio-debugging.png similarity index 100% rename from image/editor-visualstudio-debugging.png rename to images/editor-visualstudio-debugging.png diff --git a/image/editor-visualstudio-debugging2.png b/images/editor-visualstudio-debugging2.png similarity index 100% rename from image/editor-visualstudio-debugging2.png rename to images/editor-visualstudio-debugging2.png diff --git a/image/editor-visualstudio-download.png b/images/editor-visualstudio-download.png similarity index 100% rename from image/editor-visualstudio-download.png rename to images/editor-visualstudio-download.png diff --git a/image/editor-visualstudio-newproject.png b/images/editor-visualstudio-newproject.png similarity index 100% rename from image/editor-visualstudio-newproject.png rename to images/editor-visualstudio-newproject.png diff --git a/image/editor-visualstudio-quickdebug.png b/images/editor-visualstudio-quickdebug.png similarity index 100% rename from image/editor-visualstudio-quickdebug.png rename to images/editor-visualstudio-quickdebug.png diff --git a/image/editor-visualstudio-racer.png b/images/editor-visualstudio-racer.png similarity index 100% rename from image/editor-visualstudio-racer.png rename to images/editor-visualstudio-racer.png diff --git a/image/editor-visualstudio-racersc.png b/images/editor-visualstudio-racersc.png similarity index 100% rename from image/editor-visualstudio-racersc.png rename to images/editor-visualstudio-racersc.png diff --git a/image/editor-visualstudio-set-breakpoints.png b/images/editor-visualstudio-set-breakpoints.png similarity index 100% rename from image/editor-visualstudio-set-breakpoints.png rename to images/editor-visualstudio-set-breakpoints.png diff --git a/image/editor-visualstudio-setdebugger.png b/images/editor-visualstudio-setdebugger.png similarity index 100% rename from image/editor-visualstudio-setdebugger.png rename to images/editor-visualstudio-setdebugger.png diff --git a/image/editor-visualstudio-settings.png b/images/editor-visualstudio-settings.png similarity index 100% rename from image/editor-visualstudio-settings.png rename to images/editor-visualstudio-settings.png diff --git a/image/function-return-value.png b/images/function-return-value.png similarity index 100% rename from image/function-return-value.png rename to images/function-return-value.png diff --git a/image/function-statement-expression.png b/images/function-statement-expression.png similarity index 100% rename from image/function-statement-expression.png rename to images/function-statement-expression.png diff --git a/image/get-mac-os-information.png b/images/get-mac-os-information.png similarity index 100% rename from image/get-mac-os-information.png rename to images/get-mac-os-information.png diff --git a/image/high-order-function.png b/images/high-order-function.png similarity index 100% rename from image/high-order-function.png rename to images/high-order-function.png diff --git a/image/install-on-linux-check-system.png b/images/install-on-linux-check-system.png similarity index 100% rename from image/install-on-linux-check-system.png rename to images/install-on-linux-check-system.png diff --git a/image/install-on-linux-rust-success.png b/images/install-on-linux-rust-success.png similarity index 100% rename from image/install-on-linux-rust-success.png rename to images/install-on-linux-rust-success.png diff --git a/image/install-on-windows-1st.png b/images/install-on-windows-1st.png similarity index 100% rename from image/install-on-windows-1st.png rename to images/install-on-windows-1st.png diff --git a/image/project-structure.png b/images/project-structure.png similarity index 100% rename from image/project-structure.png rename to images/project-structure.png diff --git a/install/install_rust_on_linux.md b/install/install_rust_on_linux.md index 00ea440..4fc6f2e 100644 --- a/install/install_rust_on_linux.md +++ b/install/install_rust_on_linux.md @@ -1,4 +1,4 @@ -#Rust for Linux +# Rust for Linux Rust 支持主流的操作系统,Linux,Mac和 windows。 @@ -14,7 +14,7 @@ Rust 为Linux用户提供了两种安装方式: 结果如下图所示: -![check system info](../image/install-on-linux-check-system.png) +![check system info](../images/install-on-linux-check-system.png) 如上图所示,如果是 **x86_64** 则证明是64位系统,需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-x86_64-unknown-linux-gnu.tar.gz)64位安装包; @@ -30,7 +30,7 @@ Rust 为Linux用户提供了两种安装方式: 此时执行: `rustc --version`, 你会看到对应的 rust 版本信息,如下图所示: -![Success and show rust version info](../image/install-on-linux-rust-success.png) +![Success and show rust version info](../images/install-on-linux-rust-success.png) ### 2、命令行一键安装: Rust 提供简单的一键安装,命令如下: diff --git a/install/install_rust_on_mac_os.md b/install/install_rust_on_mac_os.md index 7b8180e..bd7ae0e 100644 --- a/install/install_rust_on_mac_os.md +++ b/install/install_rust_on_mac_os.md @@ -11,7 +11,7 @@ Rust 为 mac 用户提供了两种安装方式: `uname -a` -![Mac-os-inofrmatoin](../image/get-mac-os-information.png) +![Mac-os-inofrmatoin](../images/get-mac-os-information.png) 如上图红色部分所示,如果是 **x86_64** 则证明是64位系统,需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-x86_64-apple-darwin.pkg)64位安装包; 如果是**x86-32**则需要[下载](https://static.rust-lang.org/dist/rust-1.5.0-i686-apple-darwin.pkg)32位安装包 diff --git a/install/install_rust_on_windows.md b/install/install_rust_on_windows.md index 62b8751..7e36fcc 100644 --- a/install/install_rust_on_windows.md +++ b/install/install_rust_on_windows.md @@ -1,4 +1,4 @@ -#Rust for Windows +# Rust for Windows Rust 支持主流的操作系统,Linux,Mac和 Windows。 @@ -13,7 +13,7 @@ Rust在Windows上的安装和你在windows上安装其它软件一样。 ### 2、安装: 双击下载到的安装包,如下图所示: -![Mac-os-inofrmatoin](../image/install-on-windows-1st.png) +![Mac-os-inofrmatoin](../images/install-on-windows-1st.png) 默认,rust将安装到所有用户下,选择“Advanced”,可以指定安装用户和安装路径。然后点击"install"等待几分钟即可(中间可能会有安全提示,点击允许即可,如果你装了360之类的,需要小心360阻止写入注册表)。 diff --git a/install/rustup.md b/install/rustup.md index 11ea4bf..6016308 100644 --- a/install/rustup.md +++ b/install/rustup.md @@ -106,6 +106,7 @@ rustup self uninstall = YYYY-MM-DD = ``` + 如`stable-x86_64-pc-windows-msvc` `nightly-2014-12-18` `1.8.0`等都是合法的toolchain名称。 我们也可以采用[自定义toolchain](https://github.com/rust-lang-nursery/rustup.rs#working-with-custom-toolchains)配合rustup。 diff --git a/intoborrow/asref.md b/intoborrow/asref.md index 9683f80..b526e19 100644 --- a/intoborrow/asref.md +++ b/intoborrow/asref.md @@ -26,6 +26,7 @@ is_hello(s); let s = "hello".to_string(); is_hello(s); ``` + 因为 `String` 和 `&str` 都实现了 `AsRef`。 diff --git a/intoborrow/borrow.md b/intoborrow/borrow.md index 52d8409..49328fb 100644 --- a/intoborrow/borrow.md +++ b/intoborrow/borrow.md @@ -15,6 +15,7 @@ `AsRef` 更通用,更普遍,覆盖类型更多,是 `Borrow` 的超集。 举例: + ```rust use std::borrow::Borrow; diff --git a/intoborrow/cow.md b/intoborrow/cow.md index a19cd70..60e866a 100644 --- a/intoborrow/cow.md +++ b/intoborrow/cow.md @@ -73,6 +73,7 @@ fn abs_all(input: &mut Cow<[i32]>) { 题目:写一个函数,过滤掉输入的字符串中的所有空格字符,并返回过滤后的字符串。 对这个简单的问题,不用思考,我们都可以很快写出代码: + ```rust fn remove_spaces(input: &str) -> String { let mut buf = String::with_capacity(input.len()); @@ -86,6 +87,7 @@ fn remove_spaces(input: &str) -> String { buf } ``` + 设计函数输入参数的时候,我们会停顿一下,这里,用 `&str` 好呢,还是 `String` 好呢?思考一番,从性能上考虑,有如下结论: 1. 如果使用 `String`, 则外部在调用此函数的时候, @@ -121,6 +123,7 @@ fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> { } ``` + 完美解决了业务逻辑与返回值类型冲突的问题。本例可细细品味。 外部程序,拿到这个 `Cow` 返回值后,按照我们上文描述的 `Cow` 的特性使用就好了。 diff --git a/intoborrow/deref.md b/intoborrow/deref.md index f2cd1b6..4f512a9 100644 --- a/intoborrow/deref.md +++ b/intoborrow/deref.md @@ -1,4 +1,3 @@ - # Deref `Deref` 是 `deref` 操作符 `*` 的 trait,比如 `*v`。 @@ -41,6 +40,7 @@ let owned = "Hello".to_string(); // therefore, this works: foo(&owned); ``` + 因为 `String` 实现了 `Deref`。 ```rust @@ -69,6 +69,7 @@ let owned = vec![1, 2, 3]; foo(&owned); ``` + 因为 `Vec` 实现了 `Deref`。 ```rust @@ -85,6 +86,7 @@ f.foo(); (&&f).foo(); (&&&&&&&&f).foo(); ``` + 上面那几种函数的调用,效果是一样的。 diff --git a/intoborrow/into.md b/intoborrow/into.md index 4379dfe..a043971 100644 --- a/intoborrow/into.md +++ b/intoborrow/into.md @@ -32,6 +32,7 @@ fn is_hello>>(s: T) { let s = "hello".to_string(); is_hello(s); ``` + 因为 `String` 类型实现了 `Into>`。 下面拿一个实际生产中字符串作为函数参数的例子来说明。 @@ -66,6 +67,7 @@ let person = Person::new(name); ```rust let person = Person::new("Herman"); ``` + 就会报类型不匹配的错误。 好了,下面 `Into` 出场。我们可以定义结构体为 @@ -100,6 +102,7 @@ impl Person { } } ``` + 参数类型为 `S`, 是一个泛型参数,表示可以接受不同的类型。`S: Into` 表示 `S` 类型必须实现了 `Into`(约束)。而 `&str` 类型,符合这个要求。因此 `&str` 类型可以直接传进来。 而 `String` 本身也是实现了 `Into` 的。当然也可以直接传进来。 @@ -107,11 +110,13 @@ impl Person { 然后,下面 `name: name.into()` 这里也挺神秘的。它的作用是将 `name` 转换成 `String` 类型的另一个对象。当 name 是 `&str` 时,它会转换成 `String` 对象,会做一次字符串的拷贝(内存的申请、复制)。而当 name 本身是 `String` 类型时,`name.into()` 不会做任何转换,代价为零(有没有恍然大悟)。 根据参考资料,上述内容通过下面三式获得: + ```rust impl<'a> From<&'a str> for String {} impl From for T {} impl Into for T where U: From {} ``` + 更多内容,请参考: - [http://doc.rust-lang.org/std/convert/trait.Into.html](http://doc.rust-lang.org/std/convert/trait.Into.html) diff --git a/iterator/iterator.md b/iterator/iterator.md index b09de81..8c71730 100644 --- a/iterator/iterator.md +++ b/iterator/iterator.md @@ -5,11 +5,13 @@ 我们在控制语句里学习了Rust的`for`循环表达式,我们知道,Rust的for循环实际上和C语言的循环语句是不同的。这是为什么呢?因为,`for`循环不过是Rust编译器提供的语法糖! 首先,我们知道Rust有一个`for`循环能够依次对迭代器的任意元素进行访问,即: + ```rust for i in 1..10 { println!("{}", i); } ``` + 这里我们知道, (1..10) 其本身是一个迭代器,我们能对这个迭代器调用 `.next()` 方法,因此,`for`循环就能完整的遍历一个循环。 而对于`Vec`来说: @@ -19,6 +21,7 @@ for x in values { println!("{}", x); } ``` + 在上面的代码中,我们并没有显式地将一个`Vec`转换成一个迭代器,那么它是如何工作的呢?现在就打开标准库翻api的同学可能发现了,`Vec`本身并没有实现 `Iterator` ,也就是说,你无法对`Vec`本身调用 `.next()` 方法。但是,我们在搜索的时候,发现了`Vec`实现了 `IntoIterator` 的 trait。 其实,`for`循环真正循环的,并不是一个迭代器(Iterator),真正在这个语法糖里起作用的,是 `IntoIterator` 这个 trait。 @@ -120,6 +123,7 @@ fold(base, |accumulator, element| .. ) ```rust let m = (1..20).fold(1u64, |mul, x| mul*x); ``` + 需要注意的是,`fold`的输出结果的类型,最终是和`base`的类型是一致的(如果`base`的类型没指定,那么可以根据前面`m`的类型进行反推,除非`m`的类型也未指定),也就是说,一旦我们将上面代码中的`base`从 `1u64` 改成 `1`,那么这行代码最终将会因为数据溢出而崩溃! ### 适配器 diff --git a/macro/macro.md b/macro/macro.md index 1a6bbd9..7a0bee4 100644 --- a/macro/macro.md +++ b/macro/macro.md @@ -139,6 +139,7 @@ fn main() { println!("{:?}", a); } ``` + 这个例子初看起来比较复杂,我们来分析一下。 首先看 `=>` 左边,最外层是圆括号,前面说过这个括号可以是圆括号、方括号、花括号中的任意一种,只要是配对的就行。然后再看括号里面 `$(...),*` 正是刚才提到的重复模式,重复的模式是用逗号分隔的,重复的内容是 `$x:expr`,即可以匹配零次或多次用逗号分隔的表达式,例如 `vector![]` 和 `vector![3, x*x, s-t]` 都可以匹配成功。 diff --git a/match/match.md b/match/match.md index 7c0d8f8..9b47485 100644 --- a/match/match.md +++ b/match/match.md @@ -1,5 +1,6 @@ # match关键字 模式匹配,多出现在函数式编程语言之中,为其复杂的类型系统提供一个简单轻松的解构能力。比如从enum等数据结构中取出数据等等,但是在书写上,相对比较复杂。我们来看一个例子: + ```rust enum Direction { East, @@ -19,6 +20,7 @@ fn main() { }; } ``` + 这是一个没什么实际意义的程序,但是能清楚的表达出match的用法。看到这里,你肯定能想起一个常见的控制语句——`switch`。没错,match可以起到和switch相同的作用。不过有几点需要注意: 1. match所罗列的匹配,必须穷举出其所有可能。当然,你也可以用 **_** 这个符号来代表其余的所有可能性情况,就类似于switch中的`default`语句。 @@ -26,6 +28,7 @@ fn main() { 关于第二点,有的同学可能不明白。这么说吧,你可以把match整体视为一个表达式,既然是一个表达式,那么就一定能求得它的结果。因此,这个结果当然就可以被赋予一个变量咯。 看代码: + ```rust enum Direction { East, diff --git a/match/pattern.md b/match/pattern.md index b8ef517..5170a2f 100644 --- a/match/pattern.md +++ b/match/pattern.md @@ -1,9 +1,11 @@ # 模式 模式,是Rust另一个强大的特性。它可以被用在`let`和`match`表达式里面。相信大家应该还记得我们在[复合类型](../type/compound-types.md)中提到的关于在let表达式中解构元组的例子,实际上这就是一个模式。 + ```rust let tup = (0u8, 1u8); let (x, y) = tup; ``` + 而且我们需要知道的是,如果一个模式中出现了和当前作用域中已存在的同名的绑定,那么它会覆盖掉外部的绑定。比如: ```rust @@ -58,6 +60,7 @@ match point { ``` 另外,有的时候我们其实只对某些字段感兴趣,就可以用`..`来省略其他字段。 + ```rust struct Point { x: i64, @@ -175,6 +178,7 @@ match x { _ => {} } ``` + 输出: ``` @@ -194,6 +198,7 @@ match x { _ => println!("no"), } ``` + 猜一下上面代码的输出? 答案是`no`。因为guard是后置条件,是整个匹配的后置条件:所以上面的式子可以被等同写为`(4 | 5) if y`。 diff --git a/module/module.md b/module/module.md index f3c8b0c..5ba6c96 100644 --- a/module/module.md +++ b/module/module.md @@ -6,21 +6,25 @@ Rust 中,`crate` 是一个独立的可编译单元。具体说来,就是一个或一批文件(如果是一批文件,那么有一个文件是这个 crate 的入口)。它编译后,会对应着生成一个可执行文件或一个库。 执行 `cargo new foo`,会得到如下目录层级: + ``` foo ├── Cargo.toml └── src └── lib.rs ``` + 这里,`lib.rs` 就是一个 crate(入口),它编译后是一个库。一个工程下可以包含不止一个 crate,本工程只有一个。 执行 `cargo new --bin bar`,会得到如下目录层级: + ``` bar ├── Cargo.toml └── src └── main.rs ``` + 这里,`main.rs` 就是一个 crate(入口),它编译后是一个可执行文件。 @@ -48,6 +52,7 @@ mod aaa { } } ``` + 我们可以继续写如下代码: ```rust @@ -65,6 +70,7 @@ mod aaa { } } ``` + 还可以继续写: ```rust @@ -129,6 +135,7 @@ foo └── aaa.rs └── main.rs ``` + 我们在 `aaa.rs` 中,写上: ```rust @@ -136,6 +143,7 @@ pub fn print_aaa() { println!("{}", 25); } ``` + 在 `main.rs` 中,写上: ```rust @@ -265,10 +273,12 @@ use xxx:* 还是举上面那个 `a::b::c::d` 的例子。我们在 `main.rs` 中,要调用 `d`,得使用 `use a::b::c::d;` 来调用。而如果我们修改 `a/mod.rs` 文件为: `a/mod.rs` 文件内容: + ```rust pub mod b; pub use b::c::d; ``` + 那么,我们在 `main.rs` 中,就可以使用 `use a::d;` 来调用了。从这个例子来看没觉得方便多少。但是如果开发的一个库中有大量的内容,而且是在不同层次的模块中。那么,通过统一导出到一个地方,就能大大方便接口使用者。 ### 加载外部 crate @@ -278,11 +288,13 @@ pub use b::c::d; ```rust extern crate xxx; ``` + 这样来引入的。 注:要使上述引用生效,还必须在 `Cargo.toml` 的 `dependecies` 段,加上 `xxx="version num"` 这种依赖说明,详情见 `Cargo 项目管理` 这一章。 引入后,就相当于引入了一个符号 `xxx`,后面可以直接以这个 `xxx` 为根引用这个 crate 中的 item: + ```rust extern crate xxx; diff --git a/operator-overloading/operator.md b/operator-overloading/operator.md index cd9ab5a..310fa51 100644 --- a/operator-overloading/operator.md +++ b/operator-overloading/operator.md @@ -29,7 +29,9 @@ fn main() { print!("{:?}", cp3); } ``` + 输出: + ``` Complex { a: 6, b: 10.1} ``` @@ -47,11 +49,13 @@ impl Add for Point { } } ``` + ## 神奇的Output以及动态分发 有的同学会问了,这个`Output`是肿么回事?答,类型转换哟亲! 举个不太恰当的栗子,我们在现实中会出现`0.5+0.5=1`这样的算式,用Rust的语言来描述就是: 两个`f32`相加得到了一个`i8`。显而易见,Output就是为这种情况设计的。 还是看代码: + ```rust use std::ops::Add; @@ -86,6 +90,7 @@ fn main() { ``` 输出结果: + ``` Complex { a: 6, b: 10.1 } 39 @@ -95,6 +100,7 @@ Complex { a: 6, b: 10.1 } Rust的运算符是基于trait系统的,同样的,运算符可以被当成一种对范型的限制,我们可以这么要求`范型T必须实现了trait Mul`。 于是,我们得到了如下的一份代码: + ```rust use std::ops::Mul; diff --git a/ownership-system/borrowing_reference.md b/ownership-system/borrowing_reference.md index 9a035e3..0eb25f0 100644 --- a/ownership-system/borrowing_reference.md +++ b/ownership-system/borrowing_reference.md @@ -1,11 +1,13 @@ -**引用&借用(References&Borrowing)** -------------- +# 引用&借用(References&Borrowing) + 如上所示,Owership让我们改变一个变量的值变得“复杂”,那能否像其他编程语言那样随意改变变量的值呢?答案是有的。 + 所有权系统允许我们通过“Borrowing”的方式达到这个目的。这个机制非常像其他编程语言中的“读写锁”,即同一时刻,只能拥有一个“写锁”,或只能拥有多个“读锁”,不允许“写锁”和“读锁”在同一时刻同时出现。当然这也是数据读写过程中保障一致性的典型做法。只不过Rust是在编译中完成这个(Borrowing)检查的,而不是在运行时,这也就是为什么其他语言程序在运行过程中,容易出现死锁或者野指针的问题。 通过**&**符号完成Borrowing: + ```rust fn main() { let x: Vec = vec!(1i32, 2, 3); @@ -13,8 +15,10 @@ fn main() { println!("x={:?}, y={:?}", x, y); } ``` + Borrowing(**&x**)并不会发生所有权moved,所以println可以同时访问x和y。 通过引用,就可以对普通类型完成修改。 + ```rust fn main() { let mut x: i32 = 100; @@ -27,7 +31,9 @@ fn main() { ``` ###借用与引用的区别 + 借用与引用是一种相辅相成的关系,若B是对A的引用,也可称之为B借用了A。 + 很相近对吧,但是借用一词本意为要归还。所以在Rust用引用时,一定要注意应该在何处何时正确的“归回”借用/引用。 最后面的“高级”小节会详细举例。 @@ -40,7 +46,9 @@ fn main() { ###可变性 Borrowing也分“不可变借用”(默认,**&T**)和“可变借用”(**&mut T**)。 + 顾名思义,“不可变借用”是只读的,不可更新被引用的内容。 + ```rust fn main() { let x: Vec = vec!(1i32, 2, 3); @@ -56,6 +64,7 @@ fn main() { ``` 再次强调下,同一时刻只能有一个可变借用(&mut T),且被借用的变量本身必须有可变性 : + ```rust fn main() { //源变量x可变性 @@ -76,6 +85,7 @@ fn main() { ###高级例子 下面的复杂例子,进行了详细的注释,即使看不懂也没关系,可以在完成Lifetimes(生命周期)的学习后再仔细思考本例子。 + ```rust fn main() { let mut x: Vec = vec!(1i32, 2, 3); @@ -102,6 +112,7 @@ fn main() { println!("{:?}", x); //打印: [1, 2, 3, 10, 100, 1000] } ``` + ####总结 1. 借用不改变内存的所有者(Owner),借用只是对源内存的临时引用。 2. 在借用周期内,借用方可以读写这块内存,所有者被禁止读写内存;且所有者保证在有“借用”存在的情况下,不会释放或转移内存。 diff --git a/ownership-system/lifetime.md b/ownership-system/lifetime.md index 14a277a..2bb631b 100644 --- a/ownership-system/lifetime.md +++ b/ownership-system/lifetime.md @@ -1,5 +1,5 @@ -生命周期( Lifetime ) -------------- +# 生命周期( Lifetime ) + 下面是一个资源借用的例子: @@ -247,6 +247,7 @@ fn foo<'a, 'b: 'a>(x: &'a str, y: &'b str) -> &'a str { #### 推导总结 通过上面的学习相信大家可以很轻松完成Lifetime的推导,总之,记住两点: + 1. 输出值依赖哪些输入值。 2. 推导公式。 diff --git a/ownership-system/ownership_system.md b/ownership-system/ownership_system.md index 9ff10a7..a0544c4 100644 --- a/ownership-system/ownership_system.md +++ b/ownership-system/ownership_system.md @@ -3,7 +3,9 @@ ## 概述 所有权系统(Ownership System)是Rust语言最基本最独特也是最重要的特性之一。 + Rust追求的目标是内存安全与运行效率,但是它却没有golang, java, python等语言的内存垃圾回收机制GC。 + Rust语言号称,只要编译通过就不会崩溃(内存安全);拥有着零或者极小的运行时开销(运行效率)。这些优点也都得益于Rust的所有权系统。 所有权系统,包括三个重要的组成部分: @@ -16,4 +18,5 @@ Rust语言号称,只要编译通过就不会崩溃(内存安全);拥有 > **提示:** > Rust的所有权系统对很多初学者来说,可能会觉得难以理解,Rust的内存检查是在编译阶段完成,这个检查是非常严谨的,所以初学者在编译代码的时候,刚开始可能很难一次编译通过。 + > 不过不要害怕:),当你一旦了解熟悉它后你会喜欢上它,并且在日后的编程中受益颇多。所有权系统需要读者慢慢体会其中的奥秘,学习过程中也可以参考官方文档。 diff --git a/quickstart/comments-document.md b/quickstart/comments-document.md index 401e89e..3cd27b0 100644 --- a/quickstart/comments-document.md +++ b/quickstart/comments-document.md @@ -4,11 +4,13 @@ 在 Rust 里面注释分成两种,行注释和块注释。它的形式和 C 语言是一样的。 两种注释分别是: > 1. 行注释使用 `//` 放在注释前面。比如: + ``` // I love Rust, but I hate Rustc. ``` > 2. 块注释分别使用`/*`和`*/`包裹需要注释的内容。比如: + ``` /* W-Cat 是个大胖猫,N-Cat 是个高度近视猫。*/ ``` @@ -19,6 +21,7 @@ Rust 自带有文档功能的注释,分别是`///`和`//!`。支持 Markdown 2. `//!`用来描述包含它的项,一般用在模块文件的头部。 比如在 main.rs 文件中输入以下内容: +``` //! # The first line //! The second line /// Adds one to the number given. @@ -36,7 +39,7 @@ Rust 自带有文档功能的注释,分别是`///`和`//!`。支持 Markdown fn add_one(x: i32) -> i32 { x + 1 } - +``` ### 生成 html 文档 * `rustdoc main.rs` diff --git a/quickstart/control-flow.md b/quickstart/control-flow.md index fc9a007..ff8eded 100644 --- a/quickstart/control-flow.md +++ b/quickstart/control-flow.md @@ -37,6 +37,7 @@ for var in expression { code } ``` + 其中`expression`是一个迭代器 (iterator),具体的例子为`0..10` (不包含最后一个值), 或者`[0, 1, 2].iter()`。 @@ -74,6 +75,7 @@ match day { _ => println!("invalid"), } ``` + 其中`|`用于匹配多个值,`...`匹配一个范围 (包含最后一个值),并且`_`在这里是必须的, 因为`match`强制进行穷尽性检查 (exhaustiveness checking),必须覆盖所有的可能值。 如果需要得到`|`或者`...`匹配到的值,可以使用`@`绑定变量: diff --git a/quickstart/function-method.md b/quickstart/function-method.md index 430788a..94289ac 100644 --- a/quickstart/function-method.md +++ b/quickstart/function-method.md @@ -3,11 +3,13 @@ ## 函数 要声明一个函数,需要使用关键字`fn`,后面跟上函数名,比如 + ```rust fn add_one(x: i32) -> i32 { x + 1 } ``` + 其中函数参数的类型不能省略,可以有多个参数,但是最多只能返回一个值, 提前返回使用`return`关键字。Rust编译器会对未使用的函数提出警告, 可以使用属性`#[allow(dead_code)]`禁用无效代码检查。 @@ -19,6 +21,7 @@ fn diverges() -> ! { panic!("This function never returns!"); } ``` + 其中`panic!`是一个宏,使当前执行线程崩溃并打印给定信息。返回类型`!`可用作任何类型: ```rust @@ -34,6 +37,7 @@ Rust使用闭包 (closure) 来创建匿名函数: let num = 5; let plus_num = |x: i32| x + num; ``` + 其中闭包`plus_num`借用了它作用域中的`let`绑定`num`。如果要让闭包获得所有权, 可以使用`move`关键字: @@ -50,6 +54,7 @@ let mut num = 5; // 具体可见所有权(Owership)章节 assert_eq!(5, num); ``` + ## 高阶函数 Rust 还支持高阶函数 (high order function),允许把闭包作为参数来生成新的函数: diff --git a/quickstart/io-stream.md b/quickstart/io-stream.md index e1bc772..546be3c 100644 --- a/quickstart/io-stream.md +++ b/quickstart/io-stream.md @@ -5,6 +5,7 @@ 标准输入也叫作控制台输入,是常见输入的一种。 **例子1:** + ```rust use std::io; @@ -22,7 +23,9 @@ fn main() { read_input(); } ``` + **例子2:** + ```rust use std::io; fn main() { @@ -33,6 +36,7 @@ fn main() { println!("You typed: {}", input.trim()); } ``` + 这里体现了常见的标准输入的处理方式。两个例子都是声明了一个可变的字符串来保存输入的数据。 他们的不同之处在在于处理潜在输入异常的方式。 @@ -44,6 +48,7 @@ fn main() { 标准输出也叫控制台输出,Rust 里面常见的标准输出宏有 `print!` 和 `println!`。它们的区别是后者比前者在末尾多输出一个换行符。 **例子1:** + ```rust fn main() { print!("this "); @@ -57,13 +62,16 @@ fn main() { print!("this string has a newline, why not choose println! instead?\n"); } ``` + **例子2:** + ```rust fn main() { println!("hello there!"); println!("format {} arguments", "some"); } ``` + 这里两个例子都比较简单。读者可以运行一下查看输出结果对比一下他们的区别。 值得注意的是例子 2 中,`{ }` 会被 `"some"` 所替换。这是 rust 里面的一种格式化输出。 @@ -71,6 +79,7 @@ fn main() { 文件输入和标准输入都差不多,除了输入流指向了文件而不是控制台。下面例子采用了模式匹配来处理潜在的输入错误 **例子:** + ```rust use std::error::Error; use std::fs::File; @@ -104,6 +113,7 @@ fn main() { 文件输出和标准库输出也差不多,只不过是把输出流重定向到文件中。下面详细看例子。 **例子:** + ```rust // 输出文本 static LOREM_IPSUM: &'static str = diff --git a/quickstart/primitive-type.md b/quickstart/primitive-type.md index b1eb76b..91cf816 100644 --- a/quickstart/primitive-type.md +++ b/quickstart/primitive-type.md @@ -2,6 +2,7 @@ ## 变量绑定 Rust 通过 let 关键字进行变量绑定。 + ```rust fn main() { let a1 = 5; @@ -15,10 +16,12 @@ fn main() { //errer: mismatched types } ``` + 这里的 assert_eq! 宏的作用是判断两个参数是不是相等的,但如果是两个不匹配的类型,就算字面值相等也会报错。 ## 可变绑定 rust 在声明变量时,在变量前面加入 mut 关键字,变量就会成为可变绑定的变量。 + ```rust fn main() { let mut a: f64 = 1.0; @@ -38,6 +41,7 @@ fn main() { //assert_eq!(a, b); } ``` + 这里的 b 变量,绑定了 2.0f32。这是 Rust 里面值类型显式标记的语法,规定为`value`+`type`的形式。 **例如:** @@ -58,6 +62,7 @@ fn main() { 那是因为 let 绑定表达式的表达能力更强,而且 let 表达式实际上是一种模式匹配。 **例如:** + ```rust fn main() { let (a, mut b): (bool,bool) = (true, false); @@ -70,6 +75,7 @@ fn main() { assert_eq!(a, b); } ``` + 这里使用了 bool,只有true和false两个值,通常用来做逻辑判断的类型。 ## 原生类型 diff --git a/quickstart/rust-travel.md b/quickstart/rust-travel.md index 897821a..0c75b10 100644 --- a/quickstart/rust-travel.md +++ b/quickstart/rust-travel.md @@ -73,18 +73,22 @@ authors = ["YourName "] > ps: subl ./src/main.rs cargo 创建的项目,在src目录下会有一个初始化的main.rs文件,内容为: + ```rust fn main() { println!("Hello, world!"); } ``` + 现在我们编辑这个文件,改为: + ```rust fn main() { let rust = "Rust"; println!("Hello, {}!", rust); } ``` + 这里的 `let rust = "Rust"` 是把 rust 变量绑定为 "Rust" , `println!("Hello, {}!", rust);`里把 rust 变量的值代入到`"Hello, {}!"`中的`{}`。 diff --git a/quickstart/struct-enum.md b/quickstart/struct-enum.md index 197bd97..a4f043a 100644 --- a/quickstart/struct-enum.md +++ b/quickstart/struct-enum.md @@ -60,6 +60,7 @@ struct Point { y: i32, } ``` + 这是因为可变性是绑定的一个属性,而不是结构体自身的。可以使用`Cell`来模拟: ```rust diff --git a/quickstart/trait.md b/quickstart/trait.md index b6ee6c5..9443040 100644 --- a/quickstart/trait.md +++ b/quickstart/trait.md @@ -37,6 +37,7 @@ fn print_area(shape: T) { println!("This shape has an area of {}", shape.area()); } ``` + 其中函数`print_area()`中的泛型参数`T`被添加了一个名为`HasArea`的特性约束 (trait constraint), 用以确保任何实现了`HasArea`的类型将拥有一个`.area()`方法。 如果需要多个特性限定 (multiple trait bounds),可以使用`+`: @@ -59,6 +60,7 @@ fn bar(x: T, y: K) println!("{:?}", y); } ``` + 其中第二个例子使用了更灵活的`where`从句,它还允许限定的左侧可以是任意类型, 而不仅仅是类型参数。 @@ -134,6 +136,7 @@ Option的典型用法: let x: Option = Some(5); let y: Option = Some(5.0f64); ``` + 其中``部分表明它是一个泛型数据类型。当然,泛型参数也可以用于函数参数和结构体域: ```rust diff --git a/quickstart/vector-string.md b/quickstart/vector-string.md index 74c17ae..633d84a 100644 --- a/quickstart/vector-string.md +++ b/quickstart/vector-string.md @@ -5,6 +5,7 @@ Rust 使用数组存储相同类型的数据集。 `[T; N]`表示一个拥有 T 类型,N 个元素的数组。数组的大小是固定。 **例子:** + ```rust fn main() { let mut array: [i32; 3] = [0; 3]; @@ -20,10 +21,12 @@ fn main() { } } ``` + ### 动态数组 Vec 动态数组是一种基于堆内存申请的连续动态数据类型,拥有 O(1) 时间复杂度的索引、压入(push)、弹出(pop)。 **例子:** + ```rust //创建空Vec let v: Vec = Vec::new(); @@ -44,6 +47,7 @@ let mut v = vec![1, 2, 3]; let three = v[2]; v[1] = v[1] + 5; ``` + ## 字符串 Rust 里面有两种字符串类型。`String` 和 `str`。 @@ -52,6 +56,7 @@ Rust 里面有两种字符串类型。`String` 和 `str`。 常见的的字符串字面值就是 `&'static str` 类型。这是一种带有 `'static` 生命周期的 &str 类型。 **例子:** + ```rust // 字符串字面值 let hello = "Hello, world!"; @@ -66,6 +71,7 @@ let hello: &'static str = "Hello, world!"; 显然 `String` 类型也有压入和弹出。 **例子:** + ```rust // 创建一个空的字符串 let mut s = String::new(); diff --git a/rcarc/cell.md b/rcarc/cell.md index 48ac6e9..8fc633e 100644 --- a/rcarc/cell.md +++ b/rcarc/cell.md @@ -72,6 +72,7 @@ fn main() { shared_map.borrow_mut().insert("marbles", 38); } ``` + 从上例可看出,用了 `RefCell` 后,外面是 `不可变引用` 的情况,一样地可以修改被包裹的对象。 常用方法 @@ -79,6 +80,7 @@ fn main() { 不可变借用被包裹值。同时可存在多个不可变借用。 比如: + ```rust use std::cell::RefCell; @@ -89,6 +91,7 @@ let borrowed_five2 = c.borrow(); ``` 下面的例子会崩溃: + ```rust use std::cell::RefCell; use std::thread; @@ -108,6 +111,7 @@ assert!(result.is_err()); 可变借用被包裹值。同时只能有一个可变借用。 比如: + ```rust use std::cell::RefCell; @@ -117,6 +121,7 @@ let borrowed_five = c.borrow_mut(); ``` 下面的例子会崩溃: + ```rust use std::cell::RefCell; use std::thread; diff --git a/rcarc/mutex.md b/rcarc/mutex.md index 20929ff..dfe5b9e 100644 --- a/rcarc/mutex.md +++ b/rcarc/mutex.md @@ -10,6 +10,7 @@ 4. 在多线程中,`Mutex` 一般和 `Arc` 配合使用。 示例: + ```rust use std::sync::{Arc, Mutex}; use std::thread; diff --git a/rcarc/rcarc.md b/rcarc/rcarc.md index 50c08fc..50277ac 100644 --- a/rcarc/rcarc.md +++ b/rcarc/rcarc.md @@ -59,6 +59,7 @@ let strong_five: Option> = weak_five.upgrade(); 5. `Arc` 对于多线程的共享状态**几乎是必须的**(减少复制,提高性能)。 示例: + ```rust use std::sync::Arc; use std::thread; diff --git a/safe/safety.md b/safe/safety.md index 257f04c..bce68a5 100644 --- a/safe/safety.md +++ b/safe/safety.md @@ -58,10 +58,10 @@ Rust 中定义的不确定性行为有如下一些: 下面一些链接,给出了安全性更详细的讲解(部分未来会有对应的中文翻译)。 -[1] [Unsafe](http://doc.rust-lang.org/book/unsafe.html) -[2] [Meet Safe and Unsafe](http://doc.rust-lang.org/nightly/nomicon/meet-safe-and-unsafe.html) -[3] [How Safe and Unsafe Interact](http://doc.rust-lang.org/nightly/nomicon/safe-unsafe-meaning.html) -[4] [蓦然回首万事空 ————空指针漫谈](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/) +- [Unsafe](http://doc.rust-lang.org/book/unsafe.html) +- [Meet Safe and Unsafe](http://doc.rust-lang.org/nightly/nomicon/meet-safe-and-unsafe.html) +- [How Safe and Unsafe Interact](http://doc.rust-lang.org/nightly/nomicon/safe-unsafe-meaning.html) +- [蓦然回首万事空 ————空指针漫谈](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/) diff --git a/std/fs-and-path.md b/std/fs-and-path.md index e18b234..cbecda0 100644 --- a/std/fs-and-path.md +++ b/std/fs-and-path.md @@ -107,6 +107,7 @@ fn main() { ``` 调用如下: + ``` ➜ demo git:(master) ✗ ./target/debug/demo "fn main()" ../ fn main() { diff --git a/std/process.md b/std/process.md index cdea7c6..cbdc889 100644 --- a/std/process.md +++ b/std/process.md @@ -113,6 +113,7 @@ fn main() { } } ``` + 这段代码相当于给了`stdout`一个缓冲区,这个缓冲区直到我们计算完成之后才被读取,因此就不会造成乱序输出的问题了。 这边需要注意的一点是,一旦你开启了一个子进程,那么,无论你程序是怎么处理的,最后一定要记得对这个`child`调用`wait`或者`wait_with_output`,除非你显式地调用`kill`。因为如果父进程不`wait`它的话,它将会变成一个僵尸进程!!! diff --git a/testing/bench.md b/testing/bench.md index 88cd7d0..f329419 100644 --- a/testing/bench.md +++ b/testing/bench.md @@ -4,6 +4,7 @@ 通常来讲,所谓性能评测,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。 下面直接说明如何使用。 + ``` cargo new benchit cd benchit @@ -36,7 +37,9 @@ mod tests { } } ``` + 注意: + 1. 这里虽然使用了 `extern crate test;`,但是项目的 `Cargo.toml` 文件中依赖区并不需要添加对 `test` 的依赖; 2. 评测函数 `fn bench_add_two(b: &mut Bencher) {}` 上面使用 `#[bench]` 做标注,同时函数接受一个参数,`b` 就是 Rust 提供的评测器。这个写法是固定的。 @@ -45,7 +48,9 @@ mod tests { ``` cargo bench ``` + 输出结果类似如下: + ``` $ cargo bench Compiling benchit v0.0.1 (file:///home/mike/tmp/benchit) @@ -57,6 +62,7 @@ test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured ``` + 可以看到,Rust 的性能评测是以纳秒 ns 为单位。 写测评代码的时候,需要注意以下一些点: diff --git a/testing/threearchtest.md b/testing/threearchtest.md index d775c15..debb2fe 100644 --- a/testing/threearchtest.md +++ b/testing/threearchtest.md @@ -68,6 +68,7 @@ fn it_works() { assert!(false); } ``` + 运行 `cargo test`,你会得到类似下面这样的提示 ``` @@ -98,6 +99,7 @@ thread '
' panicked at 'Some tests failed', /home/steve/src/rust/src/libtes 如果你的测试函数没完成,或没有更新,或是故意让它崩溃,但为了让测试能够顺利完成,我们主动可以给测试函数加上 `#[should_panic]` 标识,就不会让 `cargo test` 报错了。 如 + ```rust #[test] #[should_panic] @@ -142,6 +144,7 @@ fn expensive_test() { ## 模块级测试 有时,我们会组织一批测试用例,这时,模块化的组织结构就有助于建立结构性的测试体系。Rust 中,可以类似如下写法: + ```rust pub fn add_two(a: i32) -> i32 { a + 2 @@ -157,6 +160,7 @@ mod tests { } } ``` + 也即在 `mod` 的上面写上 `#[cfg(test)]` ,表明这个模块是个测试模块。一个测试模块中,可以包含若干测试函数,测试模块中还可以继续包含测试模块,即模块的嵌套。 如此,就形式了结构化的测试体系,甚是方便。 @@ -186,11 +190,13 @@ fn it_works() { assert_eq!(4, adder::add_two(2)); } ``` + 这里,比如,我们 src 中,写了一个库,提供了一个 `add_two` 函数,现在进行集成测试。 首先,用 `extern crate` 的方式,引入这个库,由于是同一个项目,cargo 会自动找。引入后,就按模块的使用方法调用就行了,其它的测试标识与前面相同。 写完后,运行一下 `cargo test`,提示类似如下: + ``` $ cargo test Compiling adder v0.0.1 (file:///home/you/projects/adder) diff --git a/trait/trait-object.md b/trait/trait-object.md index 81cd8cd..bef842d 100644 --- a/trait/trait-object.md +++ b/trait/trait-object.md @@ -141,6 +141,7 @@ pub trait Clone: Sized { 总结: 如果一个`trait`方法是`object safe`的,它需要满足: + * 方法有`Self: Sized`约束, 或者 * 同时满足以下所有条件: * 没有泛型参数 @@ -148,6 +149,7 @@ pub trait Clone: Sized { * 除了`self`之外的其它参数和返回值不能使用`Self`类型 如果一个`trait`是`object-safe`的,它需要满足: + * 所有的方法都是`object-safe`的,并且 * trait 不要求 `Self: Sized` 约束 diff --git a/trait/trait.md b/trait/trait.md index 4986db4..fea8672 100644 --- a/trait/trait.md +++ b/trait/trait.md @@ -9,6 +9,7 @@ trait HasArea { fn area(&self) -> f64; } ``` + **trait**里面的函数可以没有函数体,实现代码交给具体实现它的类型去补充: ```rust @@ -33,6 +34,7 @@ fn main() { println!("circle c has an area of {}", c.area()); } ``` + **注**: **&self**表示的是**area**这个函数会将调用者的借代引用作为参数 这个程序会输出: @@ -69,6 +71,7 @@ fn foo(s: T) { println!("{:?}", s); } ``` + ``中`Debug`和`Clone`使用`+`连接,标示泛型`T`需要同时实现这两个trait。 #### where关键字 diff --git a/type/compound-types.md b/type/compound-types.md index 2faa5d4..18057c0 100644 --- a/type/compound-types.md +++ b/type/compound-types.md @@ -116,6 +116,7 @@ Rust对代码有着严格的安全控制,因此对一个变量也就有了所 但是有些时候,我只是想要持有一个(可变)引用的值怎么办? 如下代码: + ```rust struct RefBoy { loc: &i32, @@ -128,13 +129,16 @@ struct RefBoy { :6:14: 6:19 error: missing lifetime specifier [E0106] :6 loc: & i32, ``` + 这种时候,你将持有一个值的引用,因为它本身的生命周期在这个struct之外,所以对这个结构体而言,它无法准确的判断获知这个引用的生命周期,这在Rust编译器而言是不被接受的。 因此,这个时候就需要我们给这个结构体人为的写上一个生命周期,并显式地表明这个引用的生命周期。写法如下: + ```rust struct RefBoy<'a> { loc: &'a i32, } ``` + 这里解释一下这个符号`<>`,它表示的是一个`属于`的关系,无论其中描述的是*生命周期*还是*泛型*。即: `RefBoy in 'a `。最终我们可以得出个结论,`RefBoy`这个结构体,其生命周期一定不能比`'a`更长才行。 写到这里,可能有的人还是对生命周期比较迷糊,不明白其中缘由,其实你只需要知道两点即可: @@ -160,6 +164,7 @@ impl中的self,常见的有三种形式:`self`、`&self`、`&mut self` ,我 我曾经见过一个关于Rust的笑话:"你调用了一下别人,然后你就不属于你了"。 比如下面代码就会报出一个错误: + ```rust struct A { a: i32, @@ -176,11 +181,14 @@ fn main() { println!("{}", ast.a); } ``` + 错误: + ``` 13:25 error: use of moved value: `ast.a` [E0382] :13 println!("{}", ast.a); ``` + 为什么呢?因为Rust本身,在你调用一个函数的时候,如果传入的不是一个引用,那么无疑,这个参数的owner将被move掉。同理,`impl`中的`self`,如果你写的不是一个引用的话,也是会被默认的move掉哟! 那么如何避免这种情况呢?答案是`Copy`和`Clone`: @@ -241,6 +249,7 @@ Rust允许我们灵活的对一个struct进行你想要的实现,在编程的 Rust的枚举(`enum`)类型,跟C语言的枚举有点接近,然而更强大,事实上是代数数据类型(Algebraic Data Type)。 比如说,这是一个代表东南西北四个方向的枚举: + ```rust enum Direction { West, diff --git a/type/operator-and-formatting.md b/type/operator-and-formatting.md index 01ecb9b..c56c158 100644 --- a/type/operator-and-formatting.md +++ b/type/operator-and-formatting.md @@ -159,7 +159,7 @@ format!("{:?}", "Hello"); 再接下来`0`是一种特殊的填充语法,他表示用0补齐数字的空位,要注意的是,当0作用于负数的时候,比如上面例子中wayslog的体重是-81,那么你最终将得到`-0081`;当然了,什么都不写表示用空格填充啦;在这一位上,还会出现`+`、`#`的语法,使用比较诡异,一般情况下用不上。 -最后是一个组合式子`width$`,这里呢,大家很快就能认出来是表示后面key-value值对中的`width=4`。你们没猜错,这个值表示格式化完成后字符串的长度。它可以是一个精确的长度数值,也可以是一个以`$`为结尾的字符串,$前面的部分可以写一个key或者一个postion。 +最后是一个组合式子`width$`,这里呢,大家很快就能认出来是表示后面key-value值对中的`width=4`。你们没猜错,这个值表示格式化完成后字符串的长度。它可以是一个精确的长度数值,也可以是一个以`$`为结尾的字符串,`$`前面的部分可以写一个key或者一个postion。 最后,你需要额外记住的是,在width和type之间会有一个叫精度的区域(可以省略不写如例子),他们的表示通常是以`.`开始的,比如`.4`表示小数点后四位精度。最让人遭心的是,你仍然可以在这个位置引用参数,只需要和上面width一样,用`.N$`来表示一个position的参数,但是就是不能引用key-value类型的。这一位有一个特殊用法,那就是`.*`,它不表示一个值,而是表示两个值!第一个值表示精确的位数,第二个值表示这个值本身。这是一种很尴尬的用法,而且极度容易匹配到其他参数。因此,我建议在各位能力或者时间不欠缺的时候尽量把格式化表达式用标准的形式写的清楚明白。尤其在面对一个复杂的格式化字符串的时候。 diff --git a/type/string.md b/type/string.md index 24d42ed..5b07c6c 100644 --- a/type/string.md +++ b/type/string.md @@ -15,6 +15,7 @@ let x = "Hello"; let x:&'static str = "Hello"; ``` + 实际上是将 `"Hello"` 这个静态变量的引用传递给了`x`。同时,这里的字符串不可变! 字符串也支持转义字符: @@ -55,10 +56,13 @@ fn main() { use_str(&*s); } ``` + 我们来分析一下,以下部分将涉及到部分`Deref`的知识,可能需要你预习一下,如果不能理解大可跳过下一段: 首先呢, `&*`是两个符号`&`和`*`的集合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。 + 由于`String`实现了 `impl Deref for String`,这相当于一个运算符重载,所以你就能通过`*`获得一个`str`类型。但是我们知道,单独的`str`是不能在Rust里直接存在的,因此,我们需要先给他进行`&`操作取得`&str`这个结果。 + 有人说了,我发现只要用`&`一个操作符就能将使上面的编译通过。 这其实是一个编译器的锅,因为Rust的编译器会在`&`后面插入足够多的`*`来尽可能满足`Deref`这个特性。这个特性会在某些情况下失效,因此,为了不给自己找麻烦,还是将操作符写全为好。 @@ -66,6 +70,7 @@ fn main() { 需要知道的是,将`String`转换成`&str`是非常轻松的,几乎没有任何开销。但是反过来,将`&str`转换成`String`是需要在堆上请求内存的,因此,要慎重。 我们还可以将一个UTF-8编码的字节数组转换成String,如 + ```rust // 存储在Vec里的一些字节 let miao = vec![229,150,181]; diff --git a/type/types.md b/type/types.md index 8ace684..2e75b44 100644 --- a/type/types.md +++ b/type/types.md @@ -6,10 +6,12 @@ Rust自带了`bool`类型,其可能值为`true`或者`false`。 我们可以通过这样的方式去声明它: + ```rust let is_she_love_me = false; let mut is_he_love_me: bool = true; ``` + 当然,bool类型被用的最多的地方就是在`if表达式`里了。 ## char @@ -152,7 +154,9 @@ for i in &mut v1 { } ``` + 输出结果: + ``` 1 123 diff --git a/unsafe-rawpointer/raw-pointer.md b/unsafe-rawpointer/raw-pointer.md index 8783308..7c3f56d 100644 --- a/unsafe-rawpointer/raw-pointer.md +++ b/unsafe-rawpointer/raw-pointer.md @@ -3,6 +3,7 @@ **Rust**通过限制智能指针的行为保障了编译时安全,不过仍需要对指针做一些额外的操作。 `*const T`和`*mut T`在**Rust**中被称为“裸指针”。它允许别名,允许用来写共享所有权的类型,甚至是内存安全的共享内存类型如:`Rc`和`Arc`,但是赋予你更多权利的同时意味着你需要担当更多的责任: + * 不能保证指向有效的内存,甚至不能保证是非空的 * 没有任何自动清除,所以需要手动管理资源 * 是普通旧式类型,也就是说,它不移动所有权,因此**Rust**编译器不能保证不出像释放后使用这种bug @@ -12,6 +13,7 @@ ## 使用 创建一个裸指针: + ```rust let a = 1; let b = &a as *const i32; @@ -30,6 +32,7 @@ println!("{}", c); ``` `Box`的`into_raw`: + ```rust let a: Box = Box::new(10); // 我们需要先解引用a,再隐式把 & 转换成 * @@ -37,7 +40,9 @@ let b: *const i32 = &*a; // 使用 into_raw 方法 let c: *const i32 = Box::into_raw(a); ``` + 如上说所,引用和裸指针之间可以隐式转换,但隐式转换后再解引用需要使用`unsafe`: + ```rust // 显式 let a = 1; diff --git a/unsafe-rawpointer/unsafe.md b/unsafe-rawpointer/unsafe.md index 6756841..4473169 100644 --- a/unsafe-rawpointer/unsafe.md +++ b/unsafe-rawpointer/unsafe.md @@ -7,7 +7,9 @@ 因此在安全的Rust背后,还需要`unsafe`的支持。 `unsafe`块能允许程序员做的额外事情有: + * 解引用一个裸指针`*const T`和`*mut T` + ```rust let x = 5; let raw = &x as *const i32; @@ -16,6 +18,7 @@ println!("raw points at {}", points_at); ``` * 读写一个可变的静态变量`static mut` + ```rust static mut N: i32 = 5; unsafe { @@ -25,6 +28,7 @@ unsafe { ``` * 调用一个不安全函数 + ```rust unsafe fn foo() { //实现 @@ -39,6 +43,7 @@ fn main() { ## 使用`unsafe` `unsafe fn`不安全函数标示如果调用它可能会违反**Rust**的内存安全语意: + ```rust unsafe fn danger_will_robinson() { // 实现 @@ -46,6 +51,7 @@ unsafe fn danger_will_robinson() { ``` `unsafe block`不安全块可以在其中调用不安全的代码: + ```rust unsafe { // 实现 @@ -53,6 +59,7 @@ unsafe { ``` `unsafe trait`不安全trait及它们的实现,所有实现它们的具体类型有可能是不安全的: + ```rust unsafe trait Scary { } unsafe impl Scary for i32 {} @@ -61,12 +68,14 @@ unsafe impl Scary for i32 {} ## safe != no bug 对于**Rust**来说禁止你做任何不安全的事是它的本职,不过有些是编写代码时的`bug`,它们并不属于“内存安全”的范畴: + * 死锁 * 内存或其他资源溢出 * 退出未调用析构函数 * 整型溢出 使用`unsafe`时需要注意一些特殊情形: + * 数据竞争 * 解引用空裸指针和悬垂裸指针 * 读取未初始化的内存