LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 币圈百科 > 【Substrate开发教程】04 - Rust编程语言基础入门

【Substrate开发教程】04 - Rust编程语言基础入门

2020-10-13 松果 来源:区块链网络


这篇文章假设你已经配置好Rust语言的开发和运行环境,了解rustc和cargo的使用方法,如果不熟悉这些基本知识,可以先看本系列的前几篇文章,下面介绍Rust编程语言的一些最基础的入门知识

定义变量

使用let关键字声明一个变量

let a = "rust";

Rust强调安全性,默认变量是不可变的,即不可重新赋值,如下面的代码会报错

fn main(){ let a = "rust"; a = "c++"; println!("{}", a); } //报错


提示不能给不可变的变量赋值两次,这就是Rust确保安全性的设定之一:变量绑定。

println!这种名字以感叹号结尾,可以像函数一样被调用的语句,在rust中叫做宏。

使用mut关键字可以将变量设置为可变

let mut a = "rust";

修改上面的程序为

fn main(){ let mut a = "rust"; a = "c++"; println!("{}", a); } //c++

程序顺利通过编译。

变量隐藏

Rust允许重复定义变量,先定义的变量会被后定义的同名变量隐藏,实际上只有一个变量

fn main(){ let b: u32 = 1; let b: f32 = 1.1; println!("b = {}", b); } //b = 1.1

变量后的冒号和u32/f32是数据类型,下面会介绍。

定义常量

定义常量使用关键字const,常量名一般大写

const MAX: u32 = 10000;

数据类型

Rust是静态类型语言,即编译时就必须知道所有变量的类型。

Rust的数据类型有很多,这里介绍最基本的数据类型:整型、浮点型、布尔类型、字符类型、元组、数组。

1、整型

整数类型分为三类:无符号类型、有符号类型、自适应类型。

无符号类型类型名数值范围占用字节u80~28-11u160~216-12u320~232-14u640~264-18u1280~2128-116

u8类型常用于表示字节序列,常在文件I/O或网络I/O读取数据流时使用。

有符号类型类型名数值范围占用字节i8-27~27-11i16-215~215-12i32-231~231-14i64-263~263-18i128-2127~2127-116

自适应类型类型名数值范围占用字节usize0~232-1 或 0~264-14 或 8isize-231~231-1 或 -263~263-14 或 8

自适应类型的大小依赖运行程序的计算机架构,64位架构为64位,32位架构为32位。

2、浮点型

浮点型即小数类型,分为单精度浮点型(f32)、双精度浮点型(f64)

类型名数值范围占用字节小数点有效数字f32-3.4×1038~3.4×1038
4至少6位f64-1.8×10308~1.8×10308
8至少15位

3、布尔类型

类型名取值占用字节booltrue或false1

4、字符类型

类型名取值占用字节charUnicode标量值4

字符类型示例代码

fn main() { let c = 'z'; let z = '?'; let heart_eyed_cat = '????'; }

使用单引号定义char类型;

字符类型char的大小是32位,即4字节,统一使用Unicode字符;

拼音字母、中文、日文、韩文等字符,emoji表情及零长度的空白字符都是有效的char值。

5、元组(tuple)

元组是一种异构的有限序列,元组内的元素可以是不同的数据类型,元组有固定的长度。

元组示例代码

fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("The value of y is: {}", y); } //The value of y is: 6.4

let支持模式匹配,可以用来解构元组,这里把tup的值分别赋值给x,y,z三个变量。

6、数组(array)

数组是rust内建的原始集合类型,具有大小固定、元素为相同类型、默认不可变的特点。

数组示例代码

fn main() { let a = [1, 2, 3, 4, 5]; let b: [i32; 5] = [1, 2, 3, 4, 5]; let first = a[0]; let second = a[1]; let c = [3; 5]; println!("a = {:?}", a); println!("b = {:?}", b); println!("c = {:?}", c); println!("first = {}", first); println!("second = {}", second); } //a = [1, 2, 3, 4, 5] //b = [1, 2, 3, 4, 5] //c = [3, 3, 3, 3, 3] //first = 1 //second = 2

定义和初始化一个数组如果使用[i32; 5]这种形式,i32是每个元素的类型,分号后的数字5表明该数组包含5个元素;

为数组赋值使用[3; 5]这种形式,表示元素是数字3,有5个元素。

范围(range)

Rust的范围类型包括左闭右开[)和全闭[]两种区间。

range示例代码

fn main() { for i in 1..5 { println!("{}", i); //1 2 3 4 } for i in 1..=5 { println!("{}", i); //1 2 3 4 5 } }

这里使用了for in循环控制,范围使用..操作符,默认是左闭右开区间,加上=操作符则为全闭区间。

切片(slice)

切片允许引用集合中一段连续的元素序列,而不用引用整个集合。

切片示例代码

fn main() { let s = String::from("hello world"); println!("{:?}", &s[0..5]); println!("{:?}", &s[0..=4]); println!("{:?}", &s[..5]); println!("{:?}", &s[..=4]); println!("{:?}", &s[6..11]); println!("{:?}", &s[6..]); println!("{:?}", &s[..]); } //"hello" //"hello" //"hello" //"hello" //"world" //"world" //"hello world"

这里使用了借用操作符&,切片也使用..操作符,规则和range相同。

字符串

Rust中可以表示字符串的数据类型有很多,以下类型都相当于C语言中的char *

---&strString& 'static strVec<u8>&u8&[u8]&[u8; N]OsStrOsStringPathPathBufCStrCString

一般使用两种字符串类型

&str:固定长度字符串;String:可改变长度字符串;

字符串示例代码

fn main() { let h = "hello"; let s = String::new(); let mut hello = String::from("hello"); hello.push('w'); hello.push_str("orld"); println!("{}", h); println!("{}", s); println!("{}", hello); } //hello // //helloworld

字符串字面值也属于&str类型,只不过它是静态生命周期字符串& 'static str。

格式化输出

前面的代码使用println!宏进行了很多格式化输出,它还有其他输出格式:

符号含义符号含义{}占位符{:?}Debug{:o}八进制{:#?}美化的Debug{:x}十六进制小写{name}变量名占位符{:X}十六进制大写{index}下标{:p}指针{>}右对齐{:b}二进制{<}左对齐{:e}科学计数小写{.N}精度{:E}科学计数大写

格式化输出示例代码

fn main() { println!("Hello {}", "songguo"); let name = "songguo"; println!("{:p}", &name); println!("max = {}", usize::max_value()); } //Hello songguo //0x7ffe6c7eda60 //max = 18446744073709551615

函数

函数使用关键字fn进行定义,Rust支持函数式编程,函数可以作为参数和返回值使用。

定义函数:无参数,无返回值

fn fun(){ println!("this is a function"); }

定义函数:有参数,无返回值

fn fun(i: u32){ println!("this is a function: {}", i); }

定义函数:有参数,有返回值

fn fun(i: u32) -> u32{ println!("this is a function: {}", i); 6 }

函数声明返回值类型前使用操作符->,函数体内不带分号的数值6相当于return 6;

函数作为参数

fn sum(a: i32, b: i32) -> i32 { a + b } fn product(a: i32, b: i32) -> i32 { a * b } fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32{ op(a, b) }

函数作为返回值

fn inner() -> bool { true } fn outer() -> fn() -> bool { inner }

流程控制

rust的流程控制和其他编程语言类似。

选择

if...else... if...else if...else...

if语句的条件不需要打括号。

循环

loop {...} break while for...in...

流程控制示例代码1

fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {}", result); } //The result is 20

loop关键字后是表达式,表达式会返回一个值,这里返回counter*2。

流程控制示例代码2

fn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { println!("the value is: {}", element); } } //the value is: 10 //the value is: 20 //the value is: 30 //the value is: 40 //the value is: 50

使用for...in...循环遍历集合中的元素,iter()返回一个可遍历的迭代器。

所有权

Rust基本思维模型:要分清对一个资源是否拥有所有权(Ownership),或者只是借用状态(Borrowing)。

C语言完全手动管理资源的生命周期,Java交给GC管理,Rust通过Ownership&Borrowing规则走出了资源管理的第三条路。

规则

Rust中的每一个值都有一个被称为其所有者(owner)的变量;值在任一时刻有且只有一个所有者;当所有者(变量)离开作用域,这个值将被丢弃;

堆栈

编译时数据类型大小固定,分配到栈上;编译时数据类型大小不固定,分配到堆上;

复制(copy)

fn main() { let a = 1; let b = a; println!("{}, {}", a, b); } //1, 1

对于基本类型、元素为基本类型的元组,因为值存储于栈上,可以拷贝。

移动(move)

fn main() { let a = String::from("z"); let b = a; println!("{}, {}", a, b); } //报错

对于非基本类型,由于rust的所有权机制,字符串的值已经与变量a绑定,把a赋值给b,相当于所有权转移,这种其他语言中的浅拷贝在rust中是不被允许的。

克隆(clone)

fn main() { let a = String::from("z"); let b = a.clone(); println!("{}, {}", a, b); } //z, z

使用clone()函数,可以拷贝堆上的内容,即深拷贝。

借用(borrow)

fn main() { let a = String::from("z"); let b = &a; println!("{}, {}", a, b); } //z, z

借用使用操作符&,不会引起所有权转移。

结构体

rust提供三种结构体:具名结构体、元组结构体、单元结构体。

具名结构体

#[derive(Debug)] struct User { name: String, count: String, nonce: u64, active: bool, } impl User { fn get_name(&self) -> &str { &(self.name[..]) } fn get_nonce(&self) -> u64 { self.nonce } fn show() { println!("haha"); } } fn main() { let ming = User { name: String::from("xiao ming"), count: String::from("10001"), nonce: 10000, active: true, }; println!("{:?}", ming); println!("{}, {}", ming.get_name(), ming.get_nonce()); User::show(); } //User { name: "xiao ming", count: "10001", nonce: 10000, active: true } //xiao ming, 10000 //haha

使用#[derive(Debug)]注解让println!可以打印自定义类型;

定义结构体的方法要在impl关键字中单独定义,有&self参数的是成员方法,没有&self参数的是结构体方法。

元组结构体

#[derive(Debug)] struct Point(i32, i32); fn main() { let a = Point(10, 20); println!("{:?}, {}, {}", a, a.0, a.1) } //Point(10, 20), 10, 20

元组结构体使用索引访问其成员。

单元结构体

struct Empty; fn main() { let a = Empty; println!("{:p}", &a); let b = a; println!("{:p}", &b); } //0x7ffe4fdea658 //0x7ffe4fdea6b0

单元结构体没有任何字段。

枚举

Rust中的枚举相比于其他编程语言有更强大的功能。

普通枚举类型

#[derive(Debug)] enum IpAddrKind { V4, V6, } fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6; println!("{:?}, {:?}", four, six); } //V4, V6

扩展枚举类型

enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }

这个枚举有四个含有不同类型的成员:

Quit:没有关联任何数据;Move:包含一个匿名结构体;Write:包含单独一个String;ChangeColor:包含三个i32;

模式匹配

模式匹配使用关键字match,它是一个控制流运算符,允许将一个值与一系列模式相比较,并根据相匹配的模式执行相应代码,模式可由字面值、变量、通配符和许多其他内容构成。

enum Coin { Penny, Nickel, Dime, Quarter, } fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => { println!("Lucky penny!"); 1 }, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, } } fn main() { let coin = value_in_cents(Coin::Penny); println!("{}", coin); } //Lucky penny! //1

Result与Option

Result与Option是两个特定的枚举,整个std标准库和错误处理系统基于它们打造。

pub enum Result<T, E> { Ok(T), Err(E), } pub enum Option<T> { None, Some(T), }

Result/Option实现了and/or/and_then/map/filter等一系列函数式配套,用函数式风格进行错误处理;Option的take()方法牵涉到所有权的转移,可以影响API的设计;Option代表一种通用的空,空值存在于变量取值范围之中;

错误处理

Rust语言错误处理机制的特点如下:

基于Result/Option加模式匹配的错误处理方式;无try-catch,要求对代码错误做更精确仔细的处理;Rust中没有空指针(null pointer),被Option替代;

示例代码

use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => { panic!("Problem opening the file: {:?}", error) }, }; }

更多Rust学习资料

到这里抛砖引玉的介绍了Rust的一些基础入门知识,但这只是Rust语言的冰山一角,只有靠自己深入学习,下面列出更多Rust学习资源供读者自行探索:

《Rust程序设计语言》《RustPrimer》《通过例子学Rust》《深入浅出Rust》《Rust编程之道》

—-

编译者/作者:松果

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...