Rust 语法基础

notice

Rust 还没到1.0,开发很快,请看官方文档

语法基础

Rust的语法和其他C-family 语言很相似。

if while 这类的控制结构和其他类c语言最大的不同是Rust中不需要在条件加括号, 但是,代码块要用大括号。访问命名空间函数,变量要用::

简单的循环

rustfn 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,_) 第二个元素值会被丢弃。
matchpattern 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

阅读 12.6k

推荐阅读
lidashuang's note
用户专栏

Happy Hacking

11 人关注
63 篇文章
专栏主页
目录