Rust语法探究(一)

Rust内置的原生类型 (primitive types) 有以下10类:

  • 布尔类型(bool):有两个值truefalse

  • 字符类型  ( char ):表示单个Unicode字符,存储为4个字节。

  • 数值类型 :分为有符号整数 (i8, i16, i32, i64, isize)、 无符号整数 (u8, u16, u32, u64, usize) 以及浮点数 (f32, f64)。整形默认为i32,浮点型默认为f64.

  • 字符串类型:最底层的是不定长类型str,常用的是字符串切片&str和堆分配字符串String,字符串切片是静态分配,有固定大小,且不可变,堆分配字符串是可变的。

  • 数组  ( array ):具有固定大小,并且元素都是同种类型,可表示为[T; N]

  • 切片  ( slice ):引用一个数组的部分数据并且不需要拷贝,可表示为&[T]

  • 元组  ( tuple ):具有固定大小的有序列表,每个元素都有自己的类型,通过解构或者索引来获得每个元素的值。

  • 指针  ( pointer ):最底层的是裸指针*const T*mut T,但解引用它们是不安全的,必须放到unsafe块里。

  • 函数  ( fn ):具有函数类型的变量实质上是一个函数指针。

  • 元类型:即(),其唯一的值也是()

0:常量

Rust有两种不同类型的常量,这些常量可以在任何作用于定义,包括全局。两种方法都需要显示声明:

  • const:一个不可改变的值(通常用法)没有固定的内存地址。它们会被内联到用到它们的地方。

  • static:一个可能在staic声明周期内可变的变量,每一个值只有一个实例,且位于内存中的固定位置。

“string”是一个特例。它可以直接赋值给一个static变量,而不需要修改,因为它的类型签名:&'static str已经使用了'static生命周期。所有其他的引用类型必须使用'static声明做特殊注解。

static LANGUAGE: &'static str = "Rust";  
const  THRESHOLD: i32 = 10;  
 
fn is_big(n: i32) -> bool {  
   // Access constant in some function  
   n > THRESHOLD  
}  
 
fn main() {  
   let n = 16;  
 
   // Access constant in the main thread  
   println!("This is {}", LANGUAGE);  
   println!("The threshold is {}", THRESHOLD);  
   println!("{} is {}", n, if is_big(n) { "big" } else { "small" });  
 
}  

程序运行结果:

This is Rust  
The threshold is 10  
16 is big

一 :变量绑定

变量可以使用注解标注类型。数字可以可以通过后缀后者默认的方式进行注解。变量绑定 (variable bindings) 是通过let关键字声明的:

let x = 5;
let mut x = 5;
let x: i32 = 5;
let (a, b) = (3, 4);

// boolean type
let t = true;
let f: bool = false;

// char type
let c = 'c';

// numeric types
let x = 42;
let y: u32 = 123_456;
let z: f64 = 1.23e+2;
let zero = z.abs_sub(123.4);
let bin = 0b1111_0000;
let oct = 0o7320_1546;
let hex = 0xf23a_b049;

// string types
let str = "Hello, world!";
let mut string = str.to_string();

// arrays and slices
let a = [0, 1, 2, 3, 4];
let middle = &a[1..4];
let mut ten_zeros: [i64; 10] = [0; 10];

// tuples
let tuple: (i32, &str) = (50, "hello");
let (fifty, _) = tuple;
let hello = tuple.1;

// raw pointers
let x = 5;
let raw = &x as *const i32;
let points_at = unsafe { *raw };

// functionsfn 
foo(x: i32) -> i32 { x }
let bar: fn(i32) -> i32 = foo;

有几点是需要特别注意的:

  • 变量默认是不可改变的 (immutable),如果需要改变一个变量的值需要显式加上mut关键字。

  • 变量具有局部作用域,被限制在所属的代码块内,并且允许变量覆盖 (variable shadowing)。

  • Rust默认开启属性#[warn(unused_variable)],会对未使用的变量 (以_开头的除外) 发出警告。

  • Rust允许先声明变量然后再初始化,但是使用未被初始化的变量会产生一个编译时错误。

  • 数值类型可以使用_分隔符来增加可读性。

  • Rust还支持单字节字符b'H'以及单字节字符串b"Hello",仅限制于ASCII字符。 此外,还可以使用r#"..."#标记来表示原始字符串,不需要对特殊字符进行转义。

  • 使用&符号将String类型转换成&str类型很廉价, 但是使用to_string()方法将&str转换到String类型涉及到分配内存, 除非很有必要否则不要这么做。

  • 数组的长度是不可变的,动态的数组称为向量 (vector),可以使用宏vec!创建。

  • 元组可以使用==!=运算符来判断是否相同。

  • 不多于32个元素的数组和不多于12个元素的元组在值传递时是自动复制的。

  • Rust不提供原生类型之间的隐式转换,只能使用as关键字显式转换。

  • 可以使用type关键字定义某个类型的别名,并且应该采用驼峰命名法。

// explicit conversion
let decimal = 65.4321_f32;
let integer = decimal as u8;
let character = integer as char;

// type aliases
type NanoSecond = u64;
type Point = (u8, u8);

数组和切片

数组是相同类型T的一些对象的集合,存储在连续内存上。数组可以使用[ ]创建,并且他们的大小在编译时可以确定的,是它们类型签名的一部分[T;size]。

切片跟数组类似,但是他们的大小在编译时是未知的。相反,一个切片是一个two-word对象,第一个字指向数据的指针,第二个字是切片的长度。切片可以用来借用一个数组的某一部分,并拥有类型签名&[T]。

vector与slice就像String与 &str 一样。

use std::mem;

// This function borrows a slice
fn analyze_slice(slice: &[i32]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Fixed-size array (type signature is superfluous)
    let xs: [i32; 5] = [1, 2, 3, 4, 5];

    // All elements can be initialized to the same value
    let ys: [i32; 500] = [0; 500];

    // Indexing starts at 0
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // `len` returns the size of the array
    println!("array size: {}", xs.len());

    // Arrays are stack allocated
    println!("array occupies {} bytes", mem::size_of_val(&xs));

    // Arrays can be automatically borrowed as slices
    println!("borrow the whole array as a slice");
    analyze_slice(&xs);

    // Slices can point to a section of an array
    println!("borrow a section of the array as a slice");
    analyze_slice(&ys[1 .. 4]);

    // Out of bound indexing yields a panic
    println!("{}", xs[5]);
}

程序执行结果:

first element of the array: 1  
second element of the array: 2  
array size: 5  
array occupies 20 bytes  
borrow the whole array as a slice  
first element of the slice: 1  
the slice has 5 elements  
borrow a section of the array as a slice  
first element of the slice: 0  
the slice has 3 elements  
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is 5', <anon>:35  
playpen: application terminated with error code 101

二:函数

函数也有一个类型!它们看起来像这样:

fn foo(x: i32) -> i32 { x }
let x: fn(i32) -> i32 = foo;

要声明一个函数,需要使用关键字fn,后面跟上函数名,比如

fn add_one(x: i32) -> i32 {
    x + 1
}

其中函数参数的类型不能省略,可以有多个参数,但是最多只能返回一个值, 提前返回使用return关键字。Rust编译器会对未使用的函数提出警告, 可以使用属性#[allow(dead_code)]禁用无效代码检查。

Rust有一个特殊语法适用于分叉函数 (diverging function),它不返回值:

fn diverges() -> ! {
    panic!("This function never returns!");
}

其中panic!是一个宏,使当前执行线程崩溃并打印给定信息。返回类型!可用作任何类型:

let x: i32 = diverges();
let y: String = diverges();

三:Rust有三种注释:

  • 行注释 (line comments):以//开头,仅能注释一行。

  • 块注释 (block comments):以/*开头,以*/结尾,能注释多行,但是不建议使用。

  • 文档注释 (doc comments):以///或者//!开头,支持Markdown标记语言, 其中///等价于写属性#[doc = "..."]//!等价于#![doc = "/// ..."], 配合rustdoc可自动生成说明文档。

四:循环

loop使用loop关键字

loop {
    println!("Loop forever!");
}

If

If是分支 (branch) 的一种特殊形式,也可以使用elseelse if。 与C语言不同的是,逻辑条件不需要用小括号括起来,但是条件后面必须跟一个代码块。 Rust中的if是一个表达式 (expression),可以赋给一个变量:

let x = 5;
let y = if x == 5 { 10 } else { 15 };

Rust是基于表达式的编程语言,有且仅有两种语句 (statement):

  1. 声明语句 (declaration statement),比如进行变量绑定的let语句。

  2. 表达式语句 (expression statement),它通过在末尾加上分号;来将表达式变成语句, 丢弃该表达式的值,一律返回元类型()

表示式总是返回一个值,但是语句不返回值或者返回(),所以以下代码会报错:

let y = (let x = 5);
let z: i32 = if x == 5 { 10; } else { 15; };

值得注意的是,在Rust中赋值 (如x = 5) 也是一个表达式,返回元类型值()

For

Rust中的for循环与C语言的风格非常不同,抽象结构如下:

for var in expression {
    code
}

其中expression是一个迭代器 (iterator),具体的例子为0..10 (不包含最后一个值), 或者[0, 1, 2].iter()

While

Rust中的while循环与C语言中的类似。对于无限循环,Rust有一个专用的关键字loop。 如果需要提前退出循环,可以使用关键字break或者continue, 还允许在循环的开头设定标签 (同样适用于for循环):

'outer: loop {
   println!("Entered the outer loop");

   'inner: loop {
       println!("Entered the inner loop");
       break 'outer;
   }

   println!("This point will never be reached");}

println!("Exited the outer loop");

五:结构体

结构体 (struct) 是一种记录类型,所包含的每个域 (field) 都有一个名称。 每个结构体也都有一个名称,通常以大写字母开头,使用驼峰命名法。 元组结构体 (tuple struct) 是由元组和结构体混合构成,元组结构体有名称, 但是它的域没有。当元组结构体只有一个域时,称为新类型 (newtype)。 没有任何域的结构体,称为类单元结构体 (unit-like struct)。 结构体中的值默认是不可变的,需要使用mut使其可变。

// structs
struct Point {
  x: i32,
  y: i32,
}
let mut point = Point { x: 0, y: 0 };
  
// tuple structs
struct Color(u8, u8, u8);
let android_green = Color(0xa4, 0xc6, 0x39);
let (red, green, blue) = android_green;

// A tuple struct’s constructors can be used as functions.
struct Digit(i32);
let v = vec![0, 1, 2];
let d: Vec<Digit> = v.into_iter().map(Digit).collect();

// newtype: a tuple struct with only one element
struct Inches(i32);
let length = Inches(10);
let Inches(integer_length) = length;

// unit-like structs
struct Null;
let empty = Null;

一个包含..struct可以用来从其它结构体拷贝一些值或者在解构时忽略一些域:

#[derive(Default)]struct Point3d {
    x: i32,
    y: i32,
    z: i32,
 }
 let origin = Point3d::default();
 let point = Point3d { y: 1, ..origin };
 let Point3d { x: x0, y: y0, .. } = point;

需要注意,Rust在语言级别不支持域可变性 (field mutability),所以不能这么写:

struct Point {    
    mut x: i32,
    y: i32,
}

这是因为可变性是绑定的一个属性,而不是结构体自身的。可以使用Cell<T>来模拟:

use std::cell::Cell;
struct Point {
    x: i32,
    y: Cell<i32>,
}
let mut point = Point { x: 5, y: Cell::new(6) };
point.y.set(7);

此外,结构体的域默认是私有的,可以使用pub关键字将其设置成公开。

×有三种类型的结构(“structs”),可以使用struct关键字来创建:

  • 元组结构体,又名元组

  • 传统C结构体

  • 元结构体,无field,为了做成通用类型

// A unit struct  
struct Nil;  
  
// A tuple struct  
struct Pair(i32, f64);  
  
// A struct with two fields  
struct Point {  
    x: f64,  
    y: f64,  
}  
  
// Structs can be reused as fields of another struct  
#[allow(dead_code)]  
struct Rectangle {  
    p1: Point,  
    p2: Point,  
}  
  
fn main() {  
    // Instantiate a `Point`  
    let point: Point = Point { x: 0.3, y: 0.4 };  
  
    // Access the fields of the point  
    println!("point coordinates: ({}, {})", point.x, point.y);  
  
    // Destructure the point using a `let` binding  
    let Point { x: my_x, y: my_y } = point;  
  
    let _rectangle = Rectangle {  
        // struct instantiation is an expression too  
        p1: Point { x: my_y, y: my_x },  
        p2: point,  
    };  
  
    // Instantiate a unit struct  
    let _nil = Nil;  
  
    // Instantiate a tuple struct  
    let pair = Pair(1, 0.1);  
  
    // Destructure a tuple struct  
    let Pair(integer, decimal) = pair;  
  
    println!("pair contains {:?} and {:?}", integer, decimal);  
}

程序执行结果:

point coordinates: (0.3, 0.4)  
pair contains 1 and 0.1

六:枚举

Rust有一个集合类型,称为枚举 (enum),对于一个指定的名称有一组可替代的值, 其中子数据结构可以存储也可以不存储数据,需要使用::语法来获得每个元素的名称,可以使用use声明,所以不用手动指定作用域。

enum Person {  
    // An `enum` may either be `unit-like`,  
    Skinny,  
    Fat,  
    // like tuple structs,  
    Height(i32),  
    Weight(i32),  
    // or like structures.  
    Info { name: String, height: i32 }  
} 

enum Status {  
    Rich,  
    Poor,  
}  
enum Work {  
    Civilian,  
    Soldier,  
}  
// A function which takes a `Person` enum as an argument and  
// returns nothing.  
fn inspect(p: Person) {  
    // Usage of an `enum` must cover all cases (irrefutable)  
    // so a `match` is used to branch over it.  
    match p {  
        Person::Skinny    => println!("Is skinny!"),  
        Person::Fat       => println!("Is fat!"),  
        // Destructure `i` from inside the `enum`.  
        Person::Height(i) => println!("Has a height of {}.", i),  
        Person::Weight(i) => println!("Has a weight of {}.", i),  
        // Destructure `Info` into `name` and `height`.  
        Person::Info { name, height } => {  
            println!("{} is {} tall!", name, height);  
        },  
    }  
}  
  
fn main() {  
    let person = Person::Height(18);  
    let danny  = Person::Weight(10);  
    // `to_owned()` creates an owned `String` from a string slice.  
    let dave   = Person::Info { name: "Dave".to_owned(), height: 72 };  
    let john   = Person::Fat;  
    let larry  = Person::Skinny;  
  
    inspect(person);  
    inspect(danny);  
    inspect(dave);  
    inspect(john);  
    inspect(larry);  
    
    use Status::{Poor, Rich};  
    use Work::*;  
    
    // Equivalent to `Status::Poor`.  
    let status = Poor;  
    // Equivalent to `Work::Civilian`.  
    let work = Civilian;  
  
    match status {  
        // Note the lack of scoping because of the explicit `use` above.  
        Rich => println!("The rich have lots of money!"),  
        Poor => println!("The poor have no money..."),  
    }  
  
    match work {  
        // Note again the lack of scoping.  
        Civilian => println!("Civilians work!"),  
        Soldier  => println!("Soldiers fight!"),  
    }  
}

程序运行结果:

Has a height of 18.  
Has a weight of 10.  
Dave is 72 tall!  
Is fat!  
Is skinny!   
The poor have no money...  
Civilians work!

你可能感兴趣的:(Rust语法探究(一))