Cargo
cargo new guessing_game --bin
cargo build
cargo run
cargo build --release
生成一个新的工程,--bin
表示这个是一个binary工程不是一个库
crates.io 仓库,可以查找之后加到dependency中
例如如果添加了一个regex库
那么可以去使用他
use regex::Regex;
fn main() {
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
println!("Did our date match? {}", re.is_match("2014-01-01"));
}
Cargo.toml
andCargo.lock
are stored in the root of your package (package root).- Source code goes in the
src
directory. - The default library file is
src/lib.rs
. - The default executable file is
src/main.rs
.- Other executables can be placed in
src/bin/
.
- Other executables can be placed in
- Benchmarks go in the
benches
directory. - Examples go in the
examples
directory. Integration tests go in the
tests
directory.运行examples
cargo build --example binding_over_turn
cargo run --example exname -- --exoption exarg1 exarg2
会运行工程目录里的examples里面的binding_over_turn.rs
Target Selection
When no target selection options are given,
cargo run
will run the binary target. If there are multiple binary targets, you must pass a target flag to choose one. Or, thedefault-run
field may be specified in the[package]
section ofCargo.toml
to choose the name of the binary to run by default.--bin
_name_Run the specified binary.--example
_name_Run the specified example.Package Selection
By default, the package in the current working directory is selected. The
-p
flag can be used to choose a different package in a workspace.-p
spec--package
_spec_The package to run. See cargo-pkgid(1) for the SPEC format.
crate and package
A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.
module模块
A module without a body is loaded from an external file
在文件系统中,目录结构往往以斜杠在路径字符串中表示对象的位置,Rust 中的路径分隔符是 :: 。
路径分为绝对路径和相对路径。绝对路径从 crate 关键字开始描述。相对路径从 self 或 super 关键字或一个标识符开始描述。例如:
crate::nation::government::govern();
是描述 govern 函数的绝对路径,相对路径可以表示为:
nation::government::govern();
访问权限
Rust 中有两种简单的访问权:公共(public)和私有(private)。
默认情况下,如果不加修饰符,模块中的成员访问权将是私有的。
如果想使用公共权限,需要使用 pub 关键字。
对于私有的模块,只有在与其平级的位置或下级的位置才能访问,不能从其外部访问。
String
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
let foo = 5; // immutable.
let mut bar = 5; // mutable
String
是一个字符串类型,由标准库提供。String是一个可增长的,UTF-8编码的文本。String::new()
语法用了::
因为它是一个特定类型的”关联函数“。这就是说,它与String
自身关联,而不是与一个特定的String
实例关联。一些语言管这叫一个“静态方法”。
println!("You guessed: {}", guess);
这打印出我们保存输入的字符串。{}
是一个占位符,所以我们传递guess
作为一个参数。如果我们有多个{}
,我们应该传递多个参数:
Shadow
let mut gusss: string = "xxxx"
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
let mut x: i32 = 1;
x = 7;
let x = x; // `x` is now immutable and is bound to `7`
let y = 4;
let y = "I can also be bound to text!"; // `y` is now of a different type
稍等,我认为我们已经用过了一个guess
?确实,不过 Rust 允许我们用新值“遮盖(shadow)”之前的guess
。这在这种具体的情况中经常被用到,guess
开始是一个String
,不过我们想要把它转换为一个u32
。遮盖(Shadowing)让我们重用guess
名字,而不是强迫我们想出两个独特的像guess_str
和guess
,或者别的什么。
Unused var
let guess: u32 = guess.trim().parse()
let _guess: u32 = guess.trim().parse() //表示guess不适用,跟go _一样
let guess: &str = "xxxx";
let guess = "xxx"; //不指定类型
忽略错误
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};//捕捉panic
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");//会panic
这是你如何大体上从“错误就崩溃”移动到“确实处理错误”,通过从expect()
切换到一个match
语句。parse()
返回的Result
就是一个像Ordering
一样的枚举,不过在这个例子中,每个变量有一些数据与之相关:Ok
是一个成功,而Err
是一个失败。每一个都包含更多信息:成功的解析为整型,或一个错误类型。在这个例子中,我们match
为Ok(num)
,它设置了Ok
内叫做num
的值,接着在右侧返回它。在Err
的情况下,我们并不关心它是什么类型的错误,所以我们仅仅使用_
而不是一个名字。这忽略错误,并continue
造成我们进行loop
的下一次迭代。
表达式 VS 语句
Rust 主要是一个基于表达式的语言。只有两种语句,其它的一切都是表达式。
然而这又有什么区别呢?表达式返回一个值,而语句不是。这就是为什么这里我们以“不是所有控制路径都返回一个值”结束:x + 1;
语句不返回一个值。Rust 中有两种类型的语句:“声明语句”和“表达式语句”。其余的一切是表达式。
let mut y = 5;
let x = (y = 6); // `x` has the value `()`, not `6`.
fn add_one(x: i32) -> i32 {
x + 1
}
我们的函数声称它返回一个i32
,但是带上个分号,它就会返回一个()
。Rust意识到这大概不是我们想要的,并在之前我们看到的错误中建议去掉分号。
发散函数(Diverging functions)
Rust有些特殊的语法叫“发散函数”,这些函数并不返回:
fn diverges() -> ! {
panic!("This function never returns!");
}
panic!
是一个宏,类似我们已经见过的println!()
。与println!()
不同的是,panic!()
导致当前的执行线程崩溃并返回指定的信息。因为这个函数会崩溃,所以它不会返回,所以它拥有一个类型!
,它代表“发散”。
函数指针
我们也可以创建指向函数的变量绑定:
let f: fn(i32) -> i32;
f
是一个指向一个获取i32
作为参数并返回i32
的函数的变量绑定。
类型
数字
这里有一个不同数字类型的列表,以及它们在标准库中的文档:
数组
像很多编程语言一样,Rust有用来表示数据序列的列表类型。最基本的是数组,一个定长相同类型的元素列表。数组默认是不可变的。
let a = [1, 2, 3]; // a: [i32; 3]
let mut m = [1, 2, 3]; // m: [i32; 3]
数组的类型是[T; N]
。我们会在泛型部分的时候讨论这个T
标记。N
是一个编译时常量,代表数组的长度。
有一个可以将数组中每一个元素初始化为相同值的简写。在这个例子中,a
的每个元素都被初始化为0
:
let a = [0; 20]; // a: [i32; 20]
切片(Slices)
一个切片(slice)是一个数组的引用(或者“视图”)。它有利于安全,有效的访问数组的一部分而不用进行拷贝。比如,你可能只想要引用读入到内存的文件中的一行。原理上,片段并不是直接创建的,而是引用一个已经存在的变量。片段有预定义的长度,可以是可变也可以是不可变的。
在底层,slice 代表一个指向数据开始的指针和一个长度。
切片语法(Slicing syntax)
你可以用一个&
和[]
的组合从多种数据类型创建一个切片。&
表明切片类似于引用,这个我们会在本部分的后面详细介绍。带有一个范围的[]
,允许你定义切片的长度:
let a = [0, 1, 2, 3, 4];
let complete = &a[..]; // A slice containing all of the elements in `a`.
let middle = &a[1..4]; // A slice of `a`: only the elements `1`, `2`, and `3`.
片段拥有&[T]
类型。当我们涉及到泛型时会讨论这个T
。
你可以在标准库文档中找到更多关于slices
的文档。
元组(Tuples)
元组(tuples)是固定大小的有序列表。如下:
let x = (1, "hello");
这是一个长度为 2 的元组,由括号和逗号组成。下面也是同样的元组,不过注明了数据类型:
let x: (i32, &str) = (1, "hello");
如你所见,元组的类型跟元组看起来很像,只不过类型取代的值的位置。细心的读者可能会注意到元组是异质的:这个元组中有一个i32
和一个&str
。在系统编程语言中,字符串要比其它语言中来的复杂。现在,可以认为&str
是一个字符串片段(string slice),我们马上会讲到它。
你可以把一个元组赋值给另一个,如果它们包含相同的类型和数量。当元组有相同的长度时它们有相同的数量。
let mut x = (1, 2); // x: (i32, i32)
let y = (2, 3); // y: (i32, i32)
x = y;
你可以通过一个解构let(destructuring let)访问元组中的字段。下面是一个例子:
let (x, y, z) = (1, 2, 3);
println!("x is {}", x);
还记得之前我曾经说过let
语句的左侧远比一个赋值绑定强大吗?这就是证据。我们可以在let
左侧写一个模式,如果它能匹配右侧的话,我们可以一次写多个绑定。这种情况下,let
“解构”或“拆开”了元组,并分成了三个绑定。
这个模式是很强大的,我们后面会经常看到它。
你可以一个逗号来消除一个单元素元组和一个括号中的值的歧义:
(0,); // single-element tuple
(0); // zero in parentheses
元组索引(Tuple Indexing)
你也可以用索引语法访问一个元组的字段:
let tuple = (1, 2, 3);
let x = tuple.0;
let y = tuple.1;
let z = tuple.2;
println!("x is {}", x);
就像数组索引,它从0
开始,不过也不像数组索引,它使用.
,而不是[]
。
你可以在标准库文档中找到更多关于tuple
的文档。
IF
这些都是非常标准的情况。然而你也可以这么写:
let x = 5;
let y = if x == 5 {
10
} else {
15
}; // y: i32
你可以(或许也应该)这么写:
let x = 5;
let y = if x == 5 { 10 } else { 15 }; // y: i32
循环
loop
while
for 和go差不多
for x in 0..10 {
println!("{}", x); // x: i32
}
在我们的例子中,0..10
表达式取一个开始和结束的位置,然后给出一个含有这之间值得迭代器。当然它不包括上限值,所以我们的循环会打印0
到9
,而不是到10
。
更抽象的形式:
for var in expression {
code
}
对于范围(On ranges):
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
}
输出:
index = 0 and value = 5
index = 1 and value = 6
index = 2 and value = 7
index = 3 and value = 8
index = 4 and value = 9
别忘了在范围外面加上括号。
对于迭代器(On iterators):
let lines = "hello\nworld".lines();
for (linenumber, line) in lines.enumerate() {
println!("{}: {}", linenumber, line);
}
输出:
0: hello
1: world
循环标签(Loop labels)
你也许会遇到这样的情形,当你有嵌套的循环而希望指定你的哪一个break
或continue
该起作用。就像大多数语言,默认break
或continue
将会作用于最内层的循环。当你想要一个break
或continue
作用于一个外层循环,你可以使用标签来指定你的break
或continue
语句作用的循环。如下代码只会在x
和y
都为奇数时打印他们:
'outer: for x in 0..10 {
'inner: for y in 0..10 {
if x % 2 == 0 { continue 'outer; } // Continues the loop over `x`.
if y % 2 == 0 { continue 'inner; } // Continues the loop over `y`.
println!("x: {}, y: {}", x, y);
}
}
Vector
“Vector”是一个动态或“可增长”的数组,被实现为标准库类型Vec<T>
是一个泛型语句)。vector 总是在堆上分配数据。vector 与切片就像String
与&str
一样。你可以使用vec!
宏来创建它:
let v = vec![1, 2, 3, 4, 5]; // v: Vec<i32>
(与之前使用println!
宏时不一样,我们用中括号[]
配合vec!
。为了方便,Rust 允许使用上述各种情况。)
对于重复初始值有另一种形式的vec!
:
let v = vec![0; 10]; // ten zeroes
vector 将它们的内容以连续的T
的数组的形式存储在堆上,这意味着它们必须在编译时就知道T
的大小(就是存储一个T
需要多少字节)。有些类型的大小不可能在编译时就知道。为此你需要保存一个指向该类型的指针:幸好,Box类型正好适合这种情况。
另外值得注意的是必须用usize
类型的值来索引
越界访问(Out-of-bounds Access)
如果尝试访问并不存在的索引:
let v = vec![1, 2, 3];
println!("Item 7 is {}", v[7]);
那么当前的线程会 panic并输出如下信息:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 7'
如果你想处理越界错误而不是 panic,你可以使用像get或get_mut这样的方法,他们当给出一个无效的索引时返回None
:
let v = vec![1, 2, 3];
match v.get(7) {
Some(x) => println!("Item 7 is {}", x),
None => println!("Sorry, this vector is too short.")
}
迭代
可以用for
来迭代 vector 的元素。有3个版本:
let mut v = vec![1, 2, 3, 4, 5];
for i in &v {
println!("A reference to {}", i);
}
for i in &mut v {
println!("A mutable reference to {}", i);
}
for i in v {
println!("Take ownership of the vector and its element {}", i);
}
注意:你不能在使用 vector 的所有权遍历之后再次遍历它。你可以使用它的引用多次遍历 vector。例如,下面的代码不能编译。
let v = vec![1, 2, 3, 4, 5];
for i in v {
println!("Take ownership of the vector and its element {}", i);
}
for i in v {
println!("Take ownership of the vector and its element {}", i);
}
而如下代码则可以完美运行:
let v = vec![1, 2, 3, 4, 5];
for i in &v {
println!("This is a reference to {}", i);
}
for i in &v {
println!("This is a reference to {}", i);
}
所有权
对于所有的非native类型,一个复制会导致一个move, 原有变量将不可用,然而对于native类型,因为都实现了 copy trait 所以一个赋值会触发一个copy
借用
一个借用就是类似一个引用,但是不同的是,这个引用是不能改变他的值的
借用必须保证借用方和被借用方不再一个作用于,也就是变量有你没我,有我没你
&mut
引用
可以改变他的值,但前提是这个被引用的值也必须是mut的。需要使用他们(星号)来访问引用的内容。
规则
Rust 中的借用有一些规则:
第一,任何借用必须位于比拥有者更小的作用域。第二,对于同一个资源(resource)的借用,以下情况不能同时出现在同一个作用域下:
1 个或多个不可变引用(
&T
)唯一 1 个可变引用(
&mut T
)
译者注:即同一个作用域下,要么只有一个对资源 A 的可变引用(&mut T),要么有 N 个不可变引用(&T),但不能同时存在可变和不可变的引用
你可能注意到这些看起来很眼熟,虽然并不完全一样,它类似于数据竞争的定义:
当 2 个或更多个指针同时访问同一内存位置,当它们中至少有 1 个在写,同时操作并不是同步的时候存在一个“数据竞争”
通过引用,你可以拥有你想拥有的任意多的引用,因为它们没有一个在写。如果你在写,并且你需要2个或更多相同内存的指针,则你只能一次拥有一个&mut
。这就是Rust如何在编译时避免数据竞争:如果打破规则的话,我们会得到错误。
在记住这些之后,让我们再次考虑我们的例子。
当引用在它引用的变量之前声明会导致类似的问题。这是因为同一作用域中的资源以他们声明相反的顺序被释放:
let y: &i32;
let x = 5;
y = &x;
println!("{}", y);
为了解释这些,我们不得不回到Rust指导哲学的核心,内存安全,和Rust用以保证它的机制,所有权系统,和更具体的借用:
你可以拥有这两种类型借用的其中一个,但不能同时拥有:
拥有 1 个或多个不可变引用(&T)
只有 1 个可变引用(&mut T)
生命周期
'a
读作“生命周期 a”。技术上讲,每一个引用都有一些与之相关的生命周期,不过编译器在通常情况让你可以省略(也就是,省略,查看下面的生命周期省略)它们。在我们讲到它之前,让我们看看一个显式生命周期的例子:
fn bar<'a>(...)
之前我们讨论了一些函数语法,不过我们并没有讨论函数名后面的<>
。一个函数可以在<>
之间有“泛型参数”,生命周期也是其中一种。我们在本书的后面讨论其他类型的泛型。不过现在让我们着重看生命周期。
这个生命周期的语法也表明了 bar
的生命周期不能超出 'a
的周期
我们用<>
声明了生命周期。这是说bar
有一个生命周期'a
。如果我们有两个拥有不同生命周期的引用参数,它应该看起来像这样:
fn bar<'a, 'b>(...)
接着在我们的参数列表中,我们使用了我们命名的生命周期:
...(x: &'a i32)
如果我们想要一个&mut
引用,我们这么做:
...(x: &'a mut i32)
如果你对比一下&mut i32
和&'a mut i32
,他们是一样的,只是后者在&
和mut i32
之间夹了一个'a
生命周期。&mut i32
读作“一个i32
的可变引用”,而&'a mut i32
读作“一个带有生命周期’a的i32的可变引用”。
例子: 如下例子表示y的生命周期要比failed_borrow要长,所以y会在整个函数执行之后释放,但是_x已经先于他释放,所以会失败
// 不带参量的函数,不过有一个生命周期参量 `'a`。
fn failed_borrow<'a>() {
let _x = 12;
// 报错:`_x` 存活时间长度不够(`_x` does not live long enough)
let y: &'a i32 = &_x;
// 尝试使用生命周期 `'a` 作为函数内部的显式类型标注将导致失败,因为
// `&_x` 的生命周期比 `y` 的短。短生命周期不能强制转换成长生命周期。
}
impl
块
让我们在Foo
中实现一个方法:
struct Foo<'a> {
x: &'a i32,
}
impl<'a> Foo<'a> {
fn x(&self) -> &'a i32 { self.x }
}
fn main() {
let y = &5; // This is the same as `let _y = 5; let y = &_y;`.
let f = Foo { x: y };
println!("x is: {}", f.x());
}
如你所见,我们需要在impl
行为Foo
声明一个生命周期。我们重复了'a
两次,就像在函数中:impl<'a>
定义了一个生命周期'a
,而Foo<'a>
使用它。
‘static
叫做static
的生命周期是特殊的。它代表某样东西具有横跨整个程序的生命周期。大部分 Rust 程序员当他们处理字符串时第一次遇到'static
:
let x: &'static str = "Hello, world.";
基本字符串是&'static str
类型的因为它的引用一直有效:它们被写入了最终库文件的数据段。另一个例子是全局量:
static FOO: i32 = 5;
let x: &'static i32 = &FOO;
它在二进制文件的数据段中保存了一个i32
,而x
是它的一个引用。
结构体
Rust 在语言级别不支持字段可变性,所以你不能像这么写:
struct Point {
mut x: i32, // This causes an error.
y: i32,
}
更新语法(Update syntax)
一个包含..
的struct
表明你想要使用一些其它结构体的拷贝的一些值。例如:
struct Point3d {
x: i32,
y: i32,
z: i32,
}
let mut point = Point3d { x: 0, y: 0, z: 0 };
point = Point3d { y: 1, .. point };
TIP
- 如果你想要更多信息,你可以设定
RUST_BACKTRACE
环境变量来获取 backtrace - mod 中fn 默认是 private的
Ref
// 赋值语句中左边的 `ref` 关键字等价右边的 `&` 符号。
let ref ref_c1 = c;
let ref_c2 = &c;
Mut
- mut 是用来修饰变量可变性的,所以是放在变量前面
- mut 放在冒号后面,也就是用来修饰类型的时候,只是用在可变引用&mut T和裸指针*mut T上,并不能直接放在类型前面
引用计数
std::sync::Arc 线程安全的引用计数
std::sync::Rc 不是线程安全的引用计数
泛型
struct Wrapper<T> {
value: T
}
impl<T> Wrapper<T> {
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
trait
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
//可以是 &self 可以是 mut self 都能匹配和那个trait
fn append_bar(mut self) -> Self {
self.push_str("Bar");
self
}
}
sized
All type parameters have an implicit bound of Sized
. The special syntax ?Sized
can be used to remove this bound if it’s not appropriate.
try! or ?
try!
/?
是一回事,但是对于io::Result<(), Error> 不行
https://doc.rust-lang.org/std/ops/trait.Try.html