Rust入门语法
变量
Rust 是强类型语言,但具有自动判断变量类型的能力,通过关键字let,就能声明一个变量,变量的类型,由编译器推断。
Rust变量分为可变变量和不可变变量,可变变量在初始化后,可以赋值改变初始值,但是不可变变量定义后,其值就不可变了。
Rust还有重影的概念,重影是指用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。
let a = 5;
let mut b = 5;
b = 6;
let a = 6;
如上代码中,第一行代码中,定义的a变量,就是不可变变量,第二行代码中,定义的b变量就是可变变量,第三行代码即可对其重新赋值。第四行代码,则是对a变量的重影,等于消耗前一个a变量,声明一个新的a变量。
在定义变量时,rust也可以指定变量的类型。如:
let a:i32 = 5; //a被指定为i32类型
基础数据类型
整数类型
位长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
isize 和 usize 两种整数类型是用来衡量数据大小的,它们的位长度取决于所运行的目标平台,如果是 32位架构的处理器将使用 32 位位长度整型。
在定义变量时,rust也可以指定变量的类型
浮点类型
说明Rust 与其它语言一样支持 32 位浮点数(f32)和 64 位浮点数(f64)。默认情况下,Rust会用64 位浮点数,因为现代计算机处理器对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。
字符类型
Rust的 char 类型大小为 4 个字节,代表 Unicode标量值,这意味着它可以支持中文,日文和韩文字符等非英文字符甚至表情符号和零宽度空格在 Rust 中都是有效的 char 值。
布尔类型
布尔型用 bool 表示,值只能为 true 或 false,占用1个字节,8bit。
复合类型
- 元组
元组用一对 ( ) 包括的一组数据,可以包含不同种类的数据; - 数组
数组用一对 [ ] 包括的同类型数据。
let tup:(i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup; //利用元组,一次性对三个变量赋值
let a = tup.0; //使用元组的方法,从0开始对应第一个
let b = [1, 2, 3, 4, 5]; //数组长度由编译器推断
let c: [i32; 5] = [1, 2, 3, 4, 5]; //数组长度被指定
let d = [3; 5]; // 等同于 let d = [3, 3, 3, 3, 3];
let e = b[0]; //数组下标从0开始
类型转换
Rust禁止隐试类型转换,只能进行显示类型转换
let var1:i32 = 41;
let var2:i16 = var1 as i16; // 显示转换
函数
函数的基本格式是:
- fn <函数名>(<参数>)-> <返回值类型> {<函数体>}
其中,参数和返回值类型,如果没有的话,都可以省略。
fn another_function() {
println!("Hello, rust!");
}
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
fn add2(x:i32, y:i32) -> i32 {
x + y
}
在上述函数中,add2的函数体的内容没有分号表示结尾,这样写,语法上也相当于返回这个表达式的值,和add的函数体等价。
蛇形命名法(snake_case) 是指每个空格皆以底线(_)取代的书写风格,且每个单字的第一个字母皆为小写。这是rust对复杂函数名命名的要求。
程序控制逻辑
条件语句
if 条件{
} else {
}
//实例
if true {
println!("haha");
else {
println!("haha");
}
条件为bool类型, Rust 中的 if 不存在单语句不用加{} 的规则,不允许使用一个语句代替一个块。
在 Rust 中我们可以使用 if-else 结构实现类似于三元条件运算表达式 (A ? B : C) 的效果。
if else可以像C语言一样,做多选一。
let number = if a > 0 { 1 } else { -1 };
如果a大于0,number等于1,否则等于0;
循环
循环有while、for、loop三种,这三种的格式如下:
let s = ['R', 'U', 'S', 'T', '!'];
println!("loop \'T\' 的索引为 {}", location); //'T' 的索引为 3
i = 0;
while i < s.len() {
let ch = s[i];
if ch == 'T' {
break;
}
i += 1;
}
println!("while \'T\' 的索引为 {}", i);//'T' 的索引为 3
for n in 0..s.len() {
let ch = s[n];
if ch == 'T' {
i = n;
break;
}
}
println!("for \'T\' 的索引为 {}", i);//'T' 的索引为 3
let mut i = 0;
let location = loop {
let ch = s[i];
if ch == 'T' {
break i;
}
i += 1;
};
while后面的内容是条件,是bool类型。s.len()返回数值的长度,break则可以跳出循环。如果break后面带有值,则在跳出循环后返回这个值。0…s.len()表示从0到比s长度小1的全部整数。
所有权
所有权是Rust 语言为高效使用内存而设计的语法机制。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。
对于一个变量而言:
- Rust 中的每个值都有一个变量,称为其所有者。
- 一次只能有一个所有者。
- 当所有者不在作用域时,该值将被删除。
变量与数据的交互方式
变量与数据之间有两种交互方式,分别是复制和移动。
复制
仅在栈中的数据的复制方式是直接复制,这不会花费更长的时间或更多的存储空间。
这些仅在栈上的数据,是基本数据类型的数据,以及仅包含以上类型数据的元组。
移动
堆中有内容的数据复制是是移动。
比如,两个 String 对象在栈中,每个 String 对象都有一个指针指向堆中的 “hello” 字符串。在给 s2 赋值时,只有栈中的数据被复制了,堆中的字符串依然还是原来的字符串。
克隆
但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式——克隆。
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2); //s1 = hello, s2 = hello
函数的所有权机制
如果将变量当作参数传入函数,那么它和复制的效果是一样的。
fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
fn main() {
let s = String::from("hello");
// s 被声明有效
takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效
let x = 5;
// x 被声明有效
makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
引用与租借
引用不会获得值的所有权。引用只能租借(Borrow)值的所有权。引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权;当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值。
let s1 = String::from("hello");
let s2 = &s1; // 当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值;
println!("s1 is {}, s2 is {}", s1, s2);
不可变引用(&T),也被称为共享引用,所有者可以读取引用指向的数据,但不能修改数据。
可变引用(&mut T)也被称为独占引用,不能有别名,在同一时刻,同一个值不可能存在别的引用。
垂悬引用
变量引用的目标变量被释放,这个变量就是垂悬引用。
结构体
Rust中的struct结构体,和C++中的类很像,有对应的成员变量,也有对应的方法。
Struct结构体
struct Site {
domain: String,
name: String,
nation: String,
found: u32
}
let rust = Site {
domain: String::from("www.rust.com"),
name: String::from("rust"),
nation: String::from("China"),
found: 2013
};
let site = Site {
domain: String::from("www.rust.com"),
name: String::from("rust"),
..rust //..rust 后面不可以有逗号。这种语法不允许一成不变的复制另一个结构体实例,意思就是说至少重新设定一个字段的值才能引用其他实例的值。
};
最后一行中,如果被复制的结构体变量成员中,有复杂变量,那么这个结构体就是被移动,原结构体就不能被访问了;如果只有基本类型变量,则这个结构体就只是被复制,原结构体仍然能够访问。
元组结构体
元组结构体是为了处理那些需要定义类型(经常使用)又不想太复杂的简单数据 struct Color(u8, u8, u8); 元组结构体对象的使用方式和元组一样,通过 . 和下标来进行访问:
struct Color(u8, u8, u8);
struct Point(f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
println!("black = ({}, {}, {})", black.0, black.1, black.2);
println!("origin = ({}, {})", origin.0, origin.1);
println!("black is {:?}", black); //引入库:[derive(Debug)],可以用 {:?} 占位符输
出一整个结构体
println!("origin is {:#?}", black); //引入库:[derive(Debug)],属性较多的话可以使
用另一个占位符 {:#?}
元组结构体的成员的类型,是可以不同的。
输出内容时,加一个#,打印会更美观。
结构体方法和关联函数
方法(Method)和函数(Function)类似,只不过它是用来操作结构体实例的。
之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数:它在 impl 块中却没有 &self 参数。这种函数不依赖实例,但是使用它需要声明是在哪个 impl 块中的。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn create(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
} //结构体关联函数
fn area(&self) -> u32 {
self.width * self.height
} //结构体方法
}
枚举
基本语法
Rust的枚举有三种模式,分别是
enum Book {
Page, Name
}
let book = Book::Page;
enum Book {
Page(u32),
Name(String),
}
let book = Book::Page(1001);
#[derive(Debug)]
enum Book {
Page{index: u32}, // 结构体类型
Name{url:String},
}
let book = Book::Page{index: 1001};
println!("{:?}", book); //Page{ index: 1001 }
枚举中的成员变量可以被复制,然后在合适的地方使用它,如match中。
match 语法
Rust通过 match 语句来实现分支结构(许多语言中的 switch ),其语法的一般格式如下:
match 枚举类实例 { 分类1 => 返回值表达式, 分类2 => 返回值表达式, … };
#[derive(Debug)]
enum Book {
Page(u32), // 变量
Name{url: String},
}
let book = Book::Page(1001);
println!("{:?}", book); //Page(1001)
match book {
Book::Page(i) => {
println!("{}", i);
},
Book::Name{ url } => {
println!("{}", url);
}
}
let book = Book::Name{url: String::from("darren")};
println!("{:?}", book); //Page(1001)
match book {
Book::Page(i) => {
println!("{}", i);
},
Book::Name{ url } => {
println!("{}", url);
}
}
// 例外情况用下划线 _ 表示:
let t = "abc1";
match t {
"abc" => println!("Yes"),
_ => {println!("other");},
}
match中的默认情况,由"_"去匹配。
Option 枚举类
组织管理
基本概念
Rust 中有三个重要的组织概念:箱、包、模块。
- 箱(Crate):"箱"是二进制程序文件或者库文件,存在于"包"中,"箱"是树状结构的,它的树根是 编译器开始运行时编译的源文件所编译的程序。
- 包(Package):工程的实质就是一个"包",包必须由一个Cargo.toml 文件来管理,该文件描述了包的基本信息以及依赖项。
- 模块(Module):Rust 中的组织单位是模块(Module),Rust 文件的内容都是一个"难以发现"的模块。如下
对开发者来说,最常用的,就是模块。Rust的模块和C++的命名空间很像。模块的例子如下所示:
mod nation {
mod government {
fn govern() {}
}
mod congress {
fn legislate() {}
}
mod court {
fn judicial() {}
}
}
对于每个函数来说,都有其相对路径和绝对路径。对于govern,
绝对路径是:rate::nation::government::govern();
相对路径是:ation::government::govern();
Rust 文件的内容都是一个"难以发现"的模块。
// second_module.rs
pub fn message() -> String {
String::from("This is the 2nd module.")
}
// main.rs
mod second_module;
fn main() {
println!("This is the main module.");
println!("{}", second_module::message());
}
引入一个模块的方法,就是:mod 模块名;
访问权限
访问权限分为共有和私有。默认情况下,如果不加修饰符,模块中的成员访问权将是私有的。
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
}
use 关键字
use 关键字能够将模块标识符引入当前作用域。as 关键字可以为其标识符添加别名。
mod nation {
pub mod government {
pub fn govern() {
println!("nation.government.govern");
}
}
pub fn govern() {
println!("nation.govern");
}
}
use crate::nation::government::govern;
use crate::nation::govern as nation_govern;
fn main() {
nation_govern();
govern();
}
// use 关键字可以与 pub 关键字配合使用:
mod nation {
pub mod government {
pub fn govern() {
println!("nation.government.govern");
}
}
pub use government::govern;
// pub use self::government::govern;
}
fn main() {
nation::govern();
}
调用同级目录文件函数
假如Rust项目的目录如下所示
├── Cargo.lock
├── Cargo.toml
└── src
├── lib.rs
└── main.rs
main.rs调用lib.rs的方法如下所示。
//lib.rs
pub fn hello_rutst() {
let my_name = "rust";
let myage = 10; // 2012 年 1 月发布
println!("name:{}, age:{}", my_name, myage);
}
pub mod helper {
pub fn helper_rutst() {
let my_name = "rust";
let myage = 10; // 2012 年 1 月发布
println!("helper name:{}, age:{}", my_name, myage);
}
}
//main.rs
mod lib; // 默认加载lib.rs或者 lib/mod.rs
fn main() {
lib::hello_rutst();
lib::helper::helper_rutst();
}
mod lib代码,会默认加载lib.rs或者mod.rs。
调用其目录文件函数
这个Rust的项目目录如下所示
├── Cargo.lock
├── Cargo.toml
└── src
├── lib1
│ ├── config.rs
│ └── mod.rs
├── lib2
│ └── mod.rs
├── lib22.rs
└── main.rs
其对应代码如下。
//lib1/config.rs
pub fn show_version() {
println!("version: 1.0");
}
//lib1/mod.rs
pub mod config;
pub fn hello_rutst() {
let my_name = "rust";
let myage = 10; // 2012 年 1 月发布
println!("lib1 name:{}, age:{}", my_name, myage);
}
//main.rs
mod lib1;
fn main() {
lib1::config::show_version();
lib1::hello_rutst();
}
这里main.rs函数引用了lib1模块,由于lib1是一个目录,因此引用的就是lib1/mod.rs。一半来说,都会将lib1中的对外开发的模块,提前引用到mod.rs中。
println打印格式
fn main() {
println!("{}", 1); // 默认用法,打印Display
println!("{:o}", 9); // 八进制
println!("{:x}", 255); // 十六进制 小写
println!("{:X}", 255); // 十六进制 大写
println!("{:p}", &0); // 指针
println!("{:b}", 15); // 二进制
println!("{:e}", 10000f32); // 科学计数(小写)
println!("{:E}", 10000f32); // 科学计数(大写)
println!("{:?}", "test"); // 打印Debug
println!("{:#?}", ("test1", "test2")); // 带换行和缩进的Debug打印
println!("{a} {b} {b}", a = "x", b = "y"); // 命名参数
}