《RustPrimer》学习笔记(一)

《RustPrimer》在线阅读链接:https://rustcc.gitbooks.io/rustprimer/content/

初识Rust

Rust是一门系统级编程语言,被设计为保证内存和线程安全,并防止段错误。它的基本理念是“零开销抽象”。理论上来说,它的速度与C/C++同级。

Rust使用实现(implementation)、特征(trait)和结构化类型(structured type)而不是类(class)。就这一点而言,与基于继承的OO语言C++、Java有相当大的差异。而跟Haskell这类函数式语言更加接近。

Rust做到了内存安全而无需.NET和Java编程语言中实现自动垃圾收集器的开销,这是通过所有权/借用机制、生命周期、以及类型系统来达到的。

影响了Rust的流行编程语言包括C,C++,C#,Erlang,Haskell,OCaml,Ruby,Scheme和Swift等等。Rust也影响了C# 7,Elm,Idris,Swift。

Rust简史。Rust最早是Mozilla雇员Graydon Hoare的个人项目,从2009年开始,得到了Mozilla研究院的支助,2010年项目对外公布。2010~2011年间实现自举(用rust写rust)。从此以后,Rust经历了巨大的设计变化和反复(历程及其艰辛),终于在2015年5月15日发布了1.0版。Rust现在由Rust项目开发社区(https://github.com/rust-lang/rust)维护。

自15年5月1.0发布以来,涌现了大量优秀的项目(可以在github上搜索Rust查找),大公司也逐渐积极参与Rust的应用开发,以及回馈开源社区。

【注】Rust在众多区块链项目中被作为主要开发语言。比较有代表性的有:以太坊EVM实现parity-ethereum,比特币的Rust实现parity-bitcoin,cardano项目钱包和密码模块的Rust实现:rust-cardano等等。

安装Rust

Rust支持主流的OS,包括Linux,Mac和Windows。

Rust团队为Linux用户提供了两种安装方式:
1、直接下载安装包
2、命令行一键安装

【注意】如果你不想在电脑上安装Rust,但又想尝试一下Rust,推荐一个在线环境:http://play.rust-lang.org/

Rust团队为Mac用户提供了两种安装方式:
1、直接下载安装包
2、命令行一键安装

由于官网提供了很好的安装脚本或者Rust的安装包。这里就不展开。

编辑器

1、Vim
2、vscode

快速上手

Rust旅途

helloworld.rs

fn main(){
    println!("Hello World!");
}

编译helloworld.rs文件

> rustc helloworld.rs
> rustc helloworld.rs -O  # 编译优化
Hello World!

让我们来解读一下这个小程序。
1、第一行fn表示一个函数,main是这个函数的名字,花括号{}里的语句则表示这个函数的内容。
2、名字叫做main的函数有特殊的用途,那就是程序的入口,也就是说程序每次都是从这个函数开始运行。
3、函数中只有一句println!("Hello World!");,这里println!是一个Rust语言自带的宏,这个宏的功能就是打印文本(结尾会换行)。
4、在Rust语言中,分号;用来把语句分隔开,也就是说语句的末尾一般用分号作为结束标志。

现在我们修改一下helloworld.rs程序,改为:

fn main(){
    let rust    = "Rust";
    println!("Hello, {}!",rust);
}

这里let rust = "Rust";是把rust变量绑定为"Rust"println!("Hello, {}!",rust);这条语句把rust变量的值代入到"Hello, {}!"中的{}

变量绑定和原生类型

变量绑定

Rust通过let关键字进行变量绑定【let有点像数学中的令一个变量】。
main1.rs

fn main(){
    let a1 =5;  //Rust默认类型推到是i32 
    let a2:i32 =5;
    assert_eq!(a1,a2);

    let b1:u32=5;
    assert_eq!(a1,b1);
}

编译main.rs(rustc main.rs),报下面的错误:

error[E0308]: mismatched types
  --> main.rs:11:2
   |
11 |     assert_eq!(a1,b1);
   |     ^^^^^^^^^^^^^^^^^^ expected i32, found u32
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error

这里的assert_eq!宏的作用是判断两个参数是不是相等的,但如果是两个不匹配的类型,就算字面值相等也会报错。

可变绑定

rust在声明变量时,在变量前面加入mut关键字,变量就会成为可变绑定的变量。
main2.rs

fn main(){
    let mut a:f64 =1.0;
    let b =2.0f32;

    a = 2.0;
    println!("{:?}",a);

    let a =a;
    a=3.0;

    assert_eq!(a,b);
}

编译main2.rs,报下面的错误:

error[E0308]: mismatched types
  --> main.rs:16:2
   |
16 |     assert_eq!(a,b);
   |     ^^^^^^^^^^^^^^^^ expected f64, found f32
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
help: you can cast an `f32` to `f64` in a lossless way
   |
5  | if ! ( * left_val == (* right_val).into() ) {
   |                      ^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

这里的b变量,绑定了2.0f32。这是Rust里面值类型显式标记的语法,规定value+type的形式。

例如:固定大小类型

1u8 1i8
1u16 1i16
1u32 1i32
1u64 1i64

可变大小类型

1usize 1isize

浮点类型

1f32 1f64

let解构

为什么在Rust里面声明一个变量的时候要采用let绑定表达式?那是因为let绑定表达式的表达能力更强,而且let表达式实际上是一种模式匹配。

例如:

fn main(){
    let (a,mut b):(bool,bool)=(true,false);

    println!("a={:?},b={:?}",a,b);

    //a=false;  
    b=true;

    assert_eq!(a,b);
}

报错:

error[E0384]: cannot assign twice to immutable variable `a`
  --> main.rs:11:2
   |
6  |     let (a,mut b):(bool,bool)=(true,false);
   |          - first assignment to `a`
...
11 |     a=false;
   |     ^^^^^^^ cannot assign twice to immutable variable

error: aborting due to previous error

原生类型

Rust支持的原始数据类型(primitive types)有以下几类:

  • 字符类型: 表示单个Unicode字符,存储占4个字节。
  • 数值类型:分为有符号数(i6,i16,i32,i64,isize)、无符号整数(u6,u16,u32,u64,usize)以及浮点数(f32,f64)。
  • 字符串类型:最底层的是不定长类型str,更常用的是字符串切片&str和堆分配字符串String,其中字符串切片是静态分配的,有固定的大小,并且不可变,而堆分配字符串是可变的。
  • 数组:具有固定大小,并且元素都是同种种类,可表示为[T:N]。
  • 切片:引用一个数组的部分数据并且不需要拷贝,可表示为&[T]。
  • 指针:最底层的是裸指针*const T*mut T,但解引用它们是不安全的,必须放到unsafe块里。
  • 函数:具有函数类型的变量实质上是一个函数指针。
  • 元类型:即(),其唯一的值也是()

例子:

fn main(){
     //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 = 0x723a_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 = &[1..4];
    let mut ten_zeroes:[i64;10]:[0;10]

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

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

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

}

有几点需要特别注意的:

  • 数值类型可以使用_分隔符来增加可读性。
  • Rust还支持单字节符b'H'以及单字符串b”Hello”,仅限制于ASCII字符。此外,还可以使用r#"..."#标记来表示原始字符串,不需要对特殊字符进行转义。
  • 使用&符号将String类型转换成&str类型很廉价,但是使用to_string()转换到String类型涉及到分配内存,除非很有必要否则不要这么做。
  • 数组的长度是不可变的,动态数组成为Vec(Vector),可以使用宏vec!创建。
  • 元组可以使用==!=运算符来判断是否相同。
  • 不多于32个元素的数组和不多于12个元素的元组在值传递时是自动复制的。
  • Rust不提供原生类型之间的隐式转换,只能使用as关键字显式转换。-
  • 可以使用type关键字定义某个类型的别名,并且应该采用驼峰命名法。
    例子:

    //显式转换
    let decimal = 65.4321_f32;
    let integer = decimal as u8;
    let character = integer as char;

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

数组、动态数组和字符串

数组array

Rust使用数组存储相同类型的数据集。[T;N]表示一个拥有T类型,N个元素的数组。数组的大小是固定的。

//main3.rs
fn main(){
    let mut array: [i32;3] = [0;3];

    array[1]=1;
    array[2]=2;

    assert_eq!([1,2],&array[1..]);
    for x in &array     {
        println!("{} ",x);
    }
}

先编译(rustc main3.rs),再运行(./main3),打印如下:

0
1
2

我们将上面的程序稍微修改一下,修改如下:

fn main(){
    let  array:[i32;3]=[0;3];

    for x in &array     {
        println!("{} ",x);
    }
}

打印结果是:

0
0
0

动态数组Vec

动态数组是一种基于堆内存申请的连续动态数据类型,拥有O(1)的时间复杂度的索引、压入(push)、弹出(pop)。

例子:

    //创建空Vec
    let v: Vec = Vec::new();

    //使用宏创建空Vec
    let v: Vec = vec![];

    //创建包含5个元素的Vec
    let v = vec![1,2,3,4,5];

    //创建十个零
    let v = vec![0;10];

    //创建可变的Vec,并压入元素3
    let mut v = vec![1,2];
    v.push(3);

    //创建拥有两个元素的Vec,并弹出一个元素
    let mut v = vec![1,2];
    let two = v.pop();

    //创建包含三个元素的可变Vec,并索引1个值和修改一个>值
    let mut v = vec![1,2,3];
    let three = v[2];
    v[1] = v[1]+5;

字符串

Rust里面有两种字符串类型。Stringstr

&str
str类型基本上不怎么使用,通常使用&str类型,它其实是[u8]类型的切片形式&[u8]。这是一种固定大小的字符串类型。常见的字符串字面值就是&'static str类型。这是一种带有'static生命周期的&str类型。

例子:

//字符串字面量
let hello = "Hello, world!";

//附带显式类型标识
let hello: &'static str = "Hello ,World!";

String

String是一个带有vec:Vec成员的结构体,你可以理解为str类型的动态形式。它们的关系相当于[T]Vec的关系。显然String类型也有压入和弹出。

例子:

//创建一个空的字符串
let mut s = String::new();
//从`&str`类型转换为`String`类型
let mut hello = String::from("Hello, ");
//压入字符和压入字符串切片
hello.push('w');
hello.push_str("orld!");

//弹出字符
let mut s = String::from("foo");
assert_eq!(s.pop(), Some('o'));
assert_eq!(s.pop(), Some('o'));
assert_eq!(s.pop(), Some('f'));
assert_eq!(s.pop(), None);

你可能感兴趣的:(programming,languages)