notice
Rust 还没到1.0,开发很快,请看官方文档
语法基础
Rust的语法和其他C-family 语言很相似。
像if
while
这类的控制结构和其他类c语言最大的不同是Rust中不需要在条件加括号, 但是,代码块要用大括号。访问命名空间函数,变量要用::
简单的循环
rust
fn main() { /* A simple loop */ loop { // A tricky calculation if universe::recalibrate() { return; } } }
使用let
关键字创建局部变量,变量默认是不可变的。如果要创建可变变量使用 let mut
let hi = "hi";
let mut count = 0;
while count < 10 {
println!("count is {}", count);
count += 1;
}
Rust 可以自己推断出变量的类型,当然我们也可以在变量名后冒号加变量类型指定变量类型,
另外,static 变量需要在定义的时候指定类型。
static MONSTER_FACTOR: f64 = 57.8;
let monster_size = MONSTER_FACTOR * 10.0;
let monster_size: int = 50;
局部变量也可以覆盖之前的申明,比如上面的例子中monster_size
最开始是 f64
类型,
第二个monster_size 是 int
类型。编译代码的话,会有warning
hellworld.rs:27:6: 27:18 warning: unused variable: `monster_size`, #[warn(unused_variable)] on by default
hellworld.rs:27 let monster_size = MONSTER_FACTOR * 10.0;
^~~~~~~~~~~~
waring 说 monster_size
这个变量未使用(这种情况一般是程序员程序写错了的)。
如果你想声明一个未使用的变量,用不想用waring,可以在变量前加一个下划线消除waring。
例如: let _monster_size = 50;
Rust标识符以字母或者下划线开头,后边可以跟任意的字符序列,数字,或者下划线。
首选的代码风格是:对于函数,变量,模块名使用小写字母,使用下划线提高可读性,
自定义类型使用驼峰法。
let my_variable = 100;
type MyType = int; // primitive types are _not_ camel case
表达式和分号
Rust和它的前辈比如C在语法上有一个显著的区别:许多在 C 中是语句的结构在 Rust 中是表达式,这让代码可以更加简洁。例如,你或许想写一段这样的代码:
let price;
if item == "salad" {
price = 3.50;
} else if item == "muffin" {
price = 2.25;
} else {
price = 2.00;
}
但是,在Rust里,你不必重复写price:
let price =
if item == "salad" {
3.50
} else if item == "muffin" {
2.25
} else {
2.00
};
两段代码完全等价:都是通过条件判断为变量 price 赋值。注意第二段代码中没有分号。这是重点:在大括号括起来的代码块中,最后一个语句省略分号,会将最后语句的值作为此代码块的返回值。
换句话说,Rust中的分号将忽略一个表达式的值。所以如果 if 分支是 { 4;},上例中的 price 将被赋值为 () (nil 或者 void)。但是没有这个分号,每个分支将有一个不同的值,并且 price 被赋予选定分支的值。
概括一下就是,除了声明(使用 let 声明变量,fn 声明函数,任何顶层命名的项,如 traits, 枚举类型 和static )的都是一个表达式,包括函数体。
fn is_four(x: int) -> bool {
// 不需要return,最后一个表达式的结果,作为返回值
x == 4
}
原始类型和字面量
整形有 int 和 uint,是有符号整型和无符号整型,其他变体,8位,16位,32位,64位等。可以写成10进制的形式(144),16进制 (0x90)八进制 (0o70)或者二进制(0b10010000)
,每个整数类型都有相应的后缀,可以用来指示字面量类型:i 代表int、u 代表uint、i8 代表i8类型。
如果没有给整型指明后缀,Rust将根据上下文中的类型注释和函数签名来推断整数类型。如果实在没有任何类型信息,Rust将假定这个没有后缀的字面量为int型。
let a = 1; // `a` 是 `int`
let b = 10i; // `b` 是 `int`, 由于 `i` 后缀
let c = 100u; // `c` 是 `uint`
let d = 1000i32; // `d` 是 `i32`
有两种浮点数类型:f32和f64。浮点数写成0.0、1e6 或者2.1e-4这些形式。和整数一样,浮点数字面量可以推导出正确的类型。后缀f32 和f64 可以用来指定一个字面量的类型。
关键字true 和false 指定bool 类型字面量。
字符,char 类型,是4个字节存储的Unicode码点,写在两个单引号间,如’x’,和在C 中一样,Rust使用反斜杠作为字符转义符,如\n,\r 以及\t。字符串字面量,写在两个双引号之间,允许使用同样的转义符。
另一方面,原始字符串(raw string)不处理任何转义序列。它们这样写:r##"blah"##
,在字符串开始的双引号后面或者结束的双引号前面有一个或者多个#,并且可以在结束定界符前包含任何字符序列。
nil类型,写做(),有唯一值。
操作符
Rust 的操作符包括基本上没有什么特别的地方。算数运算符有*
、/
、%
、+
和-
(乘、除、取余、加、和减),-
也是一个一员前置运算符,表示负数。和C 一样,也支持位运算符>>
, <<
、&
、|
、和^
。
注意,如果应用于一个整数值,!反转所有的位(像~ 在C 中的作用一样)。
比较操作符有传统的==
、!=
、<
、>
、<=
和>=
。短路(惰性)布尔操作符被写作&&(与)和||(或)。
对于编译时的类型转化,Rust使用二元操作符as
。将表达式放在左侧,并将需要转化的类型放在右边,如果转换有意义,就将表达式的结果转换为给定的类型。通常,as
只能用于原始的整数类型或者指针,并且不能重载。transmute
可以对同样大小和对齐的类型进行转换。
let x: f64 = 4.0;
let y: uint = x as uint;
assert!(y == 4u);
语法扩展
Rust可以让你自己扩展语言没有内建的功能,语法扩展的名字都是以!
结尾的。标准库定义了一些语法扩展,其中,最有用的是 format!
,sprintf-like 的文本格式化程序,以及相关的宏家族 print!
, println!
和 write!
。
format!
借鉴了Python的语法,但包含了许多的printf规则。和printf不同的是,format!
当类型与参数不匹配的时候会返回编译时的错误。
// `{}` 会打印类型的默认格式
println!("{} is {}", "the answer", 43);
// `{:?}` 可以用于任何类型
println!("what is this thing: {:?}", mystery_object);
你可以使用宏系统定义自己的语法扩展。更多的细节,macro tutorial,注意:宏特性还没有稳定下来,慎用。
控制结构
条件语句
if/else
代码例子, 大括号是必须的:
if false {
println!("that's odd");
} else if true {
println!("right");
} else {
println!("neither true nor false");
}
if
后边的条件必须是bool
类型,没有隐式的类型转换,如果if
语句后面的代码块有返回值,返回值的类型必须是一样的。
fn signum(x: int) -> int {
if x < 0 { -1 }
else if x > 0 { 1 }
else { 0 }
}
模式匹配
Rust的match
结构比C的switch
更通用,易读。
代码:
match my_number {
0 => println!("zero"),
1 | 2 => println!("one or two"),
3..10 => println!("three to ten"),
_ => println!("something else")
}
与c的规则相反,Rust语言的match不需要用break明确的退出一个case,只有一个case会执行。
规则:
- => 后边是要执行的表达式
- 可以用管道
|
匹配多个值 -
M..N
匹配范围 -
_
会匹配任何值 - 每个case以逗号分割
-
match
必须要有能够匹配到的值,如果传入的值,不会匹配到任何case,编译不会通过 -
=>
后边也支持块表达式( block expression), 这种情况每个case后边的逗号是可选的。
match my_number {
0 => { println!("zero") }
_ => { println!("something else") }
}
模式匹配很有用的一个功能是解构(destructuring):
use std::f64;
use std::num::atan;
fn angle(vector: (f64, f64)) -> f64 {
let pi = f64::consts::PI;
match vector {
(0.0, y) if y < 0.0 => 1.5 * pi,
(0.0, _) => 0.5 * pi,
(x, y) => atan(y / x)
}
}
(f64, f64) 是一个元组,会作为match的匹配值,每个case的时候,vector会被解构,如 (0.0, y)表示匹配vector中,x是0.0的值,并且会把vector中y的值绑定到(0.0,y)中的y。(x, y) 会匹配任何两个元素的元组,元组的值会绑定到x,y 。 而(0.0,_) 第二个元素值会被丢弃。match
的pattern guard
中可以在case写 if EXPR
, angle函数中的第一个case就是,只有if 后的表达式为true的时候,case才被执行。 可以使用
variable @ pattern`将匹配到的值赋给变量,例如:
match age {
a @ 0..20 => println!("{} years old", a),
_ => println!("older than 21")
}
let
也支持模式匹配,比如:
let (a, b) = get_tuple_of_two_ints();
Loops 循环
while
表示当给定的条件是true
是,会一直循环迭代。关键字break
跳出循环,continue
跳出当前迭代,进行下一次迭代。
let mut cake_amount = 8;
while cake_amount > 0 {
cake_amount -= 1;
}
loop
表示无限循环,是while true
更好的方式:
let mut x = 5u;
loop {
x += x - 3;
if x % 5 == 0 { break; }
println!("{}", x);
}
官方tutorial: http://static.rust-lang.org/doc/master/tutorial.html