这篇文章假设你已经配置好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-116u8类型常用于表示字节序列,常在文件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×10384至少6位f64-1.8×10308~1.8×10308 8至少15位 3、布尔类型 类型名取值占用字节booltrue或false14、字符类型 类型名取值占用字节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编程之道》—- 编译者/作者:松果 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
【Substrate开发教程】04 - Rust编程语言基础入门
2020-10-13 松果 来源:区块链网络
LOADING...
相关阅读:
- 金融世界新变量——TC国际交易所2020-10-01
- 跟着阿李学知识2 比特币因为什么可以被称为主流币2020-09-26
- 胡巴币情分析:7月底埋伏EOS,今日大涨,今日重点说奶王,附带KNC和ET2020-09-25
- 比特币的分布式系统是如何记账的?2020-09-24
- 头条科普 | 学习区块链过程中的高频概念:哈希函数2020-09-23