02 - Rust基础语法及数据类型

Rust基础语法及数据类型


前言


一、Rust的基础语法

变量,基本类型,函数,注释和控制流,这些几乎是每种编程语言都具有的编程概念。

1.1 变量(variable)

Rust 是强类型语言,但具有自动判断变量类型的能力。这很容易让人与弱类型语言产生混淆。
如果要声明变量,需要使用 let 关键字。例如: let a = 1;
但是在Rust中,此时声明的a是一个不可变的变量,即a的值不能更改。
声明let a = 1之后如下三种语句都是错误的

a = “hello;	
a = 1.21;
a = 2;

第一行的错误在于当声明 a 是 1 以后,a 就被确定为整型数字,不能把字符串类型的值赋给它。
第二行的错误在于自动转换数字精度有损失,Rust 语言不允许精度有损失的自动数据类型转换。
第三行的错误在于 a 不是个可变变量。
前两种错误很容易理解,但第三个是什么意思?难道 a 不是个变量吗?Rust 语言为了高并发安全而做的设计:在语言层面尽量少的让变量的值可以改变。所以 a 的值不可变。但这不意味着 a 不是"变量"(英文中的 variable),官方文档称 a 这种变量为"不可变变量"。
如果我们编写的程序的一部分在假设值永远不会改变的情况下运行,而我们代码的另一部分在改变该值,那么代码的第一部分可能就不会按照设计的意图去运转。由于这种原因造成的错误很难在事后找到。这是 Rust 语言设计这种机制的原因。
当然,使变量变得"可变"(mutable)只需一个 mut 关键字。
如下是声明可变变量,并更改变量的值:

let mut a = 1;
a = 2;

1.2 常量与不可变量的区别

既然不可变变量是不可变的,那不就是常量吗?为什么叫变量?
变量和常量还是有区别的。在 Rust 中,以下程序是合法的:

let a = 11;
let a = 22;

a如果定义是常量则不合法:

const a: i32 = 11;
let a = 22;

变量的值可以"重新绑定",但在"重新绑定"以前不能私自被改变,这样可以确保在每一次"绑定"之后的区域里编译器可以充分的推理程序逻辑。 虽然 Rust 有自动判断类型的功能,但有些情况下声明类型更加方便:

let a:u64 = 11;

这里定义a为无符号64为整型的变量,如果没有声明类型,比如let a = 11;a将被自动判断为有符号的32位整型变量,即等同于let a:i32 = 11;

1.3 重影(Shadowing)

重影的概念与其他面向对象语言里的"重写"(Override)或"重载"(Overload)是不一样的。重影就是刚才讲述的所谓"重新绑定",之所以加引号就是为了在没有介绍这个概念的时候代替一下概念。

重影就是指变量的名称可以被重新使用的机制:

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x);
}

这段程序的运行结果:

The value of x is: 12

重影与可变变量的赋值不是一个概念,重影是指用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。但可变变量赋值仅能发生值的变化。

let mut s = "123";
s = s.len();

这段程序会出错:不能给字符串变量赋整型值。

二、基本数据类型

Rust的基本数据类型包括布尔类型、整数类型、浮点类型和字符类型。

2.1 布尔(bool)类型

布尔类型在Rust中表示逻辑值,取值范围为truefalse,Rust语言中布尔类型的大小为1个字节。

fn main() {
    let istrue: bool = true;
    let isfalse: bool = false;

    println!("istrue: {}", is_true);
    println!("isfalse: {}", is_false);
}

2.2 整数类型

Rust提供了多种整数类型,包括有符号和无符号整数类型。有符号整数类型可以表示正数、负数和零,而无符号整数类型只能表示非负数和零。

i8:有符号8位整数类型
u8:无符号8位整数类型
i16:有符号16位整数类型
u16:无符号16位整数类型
i32:有符号32位整数类型
u32:无符号32位整数类型
i64:有符号64位整数类型
u64:无符号64位整数类型
i128:有符号128位整数类型
u128:无符号128位整数类型
isize:有符号指针大小整数类型
usize:无符号指针大小整数类型
整型变量使用示例:

fn main() {
    let a: i8 = 42;
    let b: u16 = 100;
    let c: i32 = -500;
    let d: u64 = 1000;
    
    println!("a: {}", a);
    println!("b: {}", b);
    println!("c: {}", c);
    println!("d: {}", d);
}

2.3 浮点类型

Rust提供了两种浮点类型:f32和f64,分别表示单精度和双精度浮点数。f32是32位浮点数,f64是64位浮点数。
浮点类型使用的示例:

fn main() {
    let x: f32 = 3.14;
    let y: f64 = 2.71828;

    println!("x: {}", x);
    println!("y: {}", y);
}

2.4 字符类型

在Rust中,字符类型用char表示,它是Unicode标量值的32位表示。字符类型的大小为4个字节。
以下是一个字符类型的示例:

fn main() {
    let c: char = 'A';
    let heart_emoji: char = '❤';

    println!("c: {}", c);
    println!("heart_emoji: {}", heart_emoji);
}

三 复合数据类型

Rust提供了几种复合数据类型,包括元组、数组和字符串。

3.1 元组(Tuple)

元组是Rust中的一种复合数据类型,它可以将多个不同类型的值组合在一起。元组使用圆括号()表示,其中的值可以通过索引访问。
以下是一个元组的示例:、

    //元组, 多类型,每个位置类型必须一一对应
    let person:(String, i32, bool,i64) = ("张三".to_string(), 100, false, 100);
    println!("Name = {}", person.0);
    println!("Age = {}", person.1);
    println!("Yes or No = {}", person.2);
    println!("weight = {}", person.3);

3.2 数组(Array)

数组是一种固定长度的数据结构,它可以存储相同类型的多个值。在Rust中,数组的长度是固定的,且数组的类型由元素类型和长度决定。
以下是一个数组的示例:

    //数组
    let array: [i32; 5] = [1,2,3,4,5];
    //for 循环遍历
    for num in array.iter() {
    	println!("Number : {}", num);
    }

3.3 字符串(String)

字符串是一种文本数据类型,它由一系列Unicode字符组成。在Rust中,字符串类型使用String表示,它是一个可增长的、可变的字符串类型。
以下是一个字符串的示例:

fn main() {
    let message: String = String::from("Hello, Rust!");
    println!("Message: {}", message);

    //字符串
	let mut str1 = String::new();//创建一个空的 可变的 字符串变量
	str1.push_str("hello");//向String 对象追加字符串
	println!("str1 : {}", str1);
}

四 自定义数据类型

Rust允许用户自定义数据类型,包括结构体和枚举。

4.1 结构体(Struct)

结构体是一种自定义的数据类型,它可以将多个不同类型的值组合在一起形成一个新的类型。结构体使用struct关键字定义,并可以包含字段(field)和方法(method)。

结构体(structure,缩写成 struct)有 3 种类型,使用 struct 关键字来创建:
1.元组结构体(tuple struct),事实上就是具名元组。

struct Pair(i32, f32);

fn main() {
    let pair = Pair(1, 0.1);
    println!("pair contains {:?} and {:?}", pair.0, pair.1);
	//pair contains 1 and 0.1
    // 元组结构体的解构
    let Pair(integer, decimal) = pair;
    println!("pair contains {:?} and {:?}", integer, decimal);
    //pair contains 1 and 0.1
}

2.经典的 C 语言风格结构体。

#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}

fn main() {
    let name = String::from("Peter");
    let age = 27;
    let peter = Person { name, age };

    println!("{:?}", peter);
    println!("person name: {}, person age: {}", peter.name, peter.age);
}
//Person { name: "Peter", age: 27 }
//person name: Peter, person age: 27

3.单元结构体(unit struct),不带字段,在泛型中很有用。

#[derive(Debug)]
struct Unit;

fn main() {
    let unit = Unit;
    println!("{:?}", unit)
    //Unit
}

以下是利用impl关键字来定义结构体成员方法 示例1:

// 利用结构体定义成员变量
struct Fruit {
    color: String,
    weight: f32
}
// 利用impl关键字来定义结构体成员方法
impl Fruit {
    fn printInfo(&self) {
        println!("{},{}",self.color,self.weight);
    }
}
// 调用
fn main() {
    let f = Fruit{color:String::from("green"), weight:12.5};
    f.printInfo();
}

问题来了,一般定义类前面都会有个关键字new,那么这个该怎么实现呢?看下面的例子。
Rust 定义标准类

// 利用结构体定义成员变量
struct Fruit {
    color: String,
    weight: f32
}
// 利用impl关键字来定义结构体成员方法
impl Fruit {
    // 相当于方法Fruit::new()调用
    fn new(color: String, weight:f32) -> Fruit {
        Fruit {
            color: color,
            weight: weight
        }
    }
    
    fn printInfo(&self) {
        println!("{},{}",self.color,self.weight);
    }
}
// 调用
fn main() {
    let f = Fruit::new(String::from("green"), 12.5);
    f.printInfo();
}

为什么在定义类方法printInfo(&self)里面传的参数是&self ?
解释:
impl关键字在struct、enum或者trait对象上实现方法调用语法
关联函数 (associated function) 的第一个参数通常为self参数。
有几种变体:

self,允许实现者移动和修改对象,对应的闭包特性为FnOnce
&self,既不允许实现者移动对象也不允许修改,对应的闭包特性为Fn
&mut self,允许实现者修改对象但不允许移动,对应的闭包特性为FnMut
不含self参数的关联函数称为静态方法 (static method

以下是结构体的示例2:

struct Girl {
	name: String,
	age: u8,
	height: f32,
}

fn struct_example1() {
	//创建一个可变的结构体对象 girl。
	let mut girl = Girl { name: "jack".to_string(), age: 12, height: 1.8};
	girl.name = "mark".to_string();
	println!("name: {}, age: {}, height :{}", girl.name, girl.age, girl.height);

	girl.name = String::from("mark");//String 变量的两种不同初始化方式
	println!("name: {}, age: {}, height :{}", girl.name, girl.age, girl.height);

	//创建一个不可变的结构体对象 girl2,  Rust不允许只将一个不可变的结构体中某个字段标记为可变。
	let girl2 = Girl { name: "jack".to_string(), age: 13, height: 1.85};
	girl2.name = "mark".to_string();//这里会报错,不能通过不可变结构体对象修改结构体中字段的值
	println!("name: {}, age: {}, height :{}", girl2.name, girl2.age, girl2.height);

}

fn main() {
	struct_example1();
}

以下是结构体的示例3:

struct User {
	active: bool,
	username: String,
	email: String,
	sign_in_count: u64,
}
 
// 构建结构体User
fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}
 
fn main() {
    let user1 = build_user(
		String::from("username123@example.com"),
		String::from("username123"),
    );
    println!("email :{}, username: {}, active: {}, sign_in_count: {}", user1.email, user1.username, user1.active, user1.sign_in_count);
}

从其他实例创建实例
通过旧的结构体实例创建一个新的实例,使用了大多数旧实例的值,但更改了一些值。可以使用结构体更新语法来完成此操作。如下所示:

  let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };

使用结构体更新语法,我们可以使用更少的代码实现同样的效果。语法..。指定未显式设置的其余字段应具有与给定实例中的字段相同的值。如下所示:

// 使用更新语法..构建结构体实例  
let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };

4.2 枚举(enum)

rust的枚举比C/C++的枚举要更加强大。它允许你列举可能的成员来定义一个枚举类型,而这些成员的类型并不要求一致。枚举类型是一个类型,它会包含所有可能的枚举成员, 而枚举值是该类型中的具体某个成员的实例。

枚举的值存储为整数,默认从0开始,后面的值依次 +1。如果想更改某个枚举的值,需要告诉它用什么整数。示例代码如下:

// 为了可以格式化打印枚举值,需要加入下面这行代码,具体有关 trait 的知识将在后面介绍。
#[derive(Debug)]
enum Week {
    Mon,
    Tue,
    Wed,
    Thu = 300,
    Fri,
    Sat,
    Sun
}

fn main() {
    // 打印枚举类型
    println!("{:?}", Week::Wed);

    // 打印枚举的值
    println!("{}", Week::Wed as i32);

    println!("{}", Week::Mon as i32);

    // 由于Thu 赋值维 300, 则后面的值依次+1
    println!("{}", Week::Fri as i32);
}
// 输出结果:
// Wed
// 2
// 0
// 301

枚举 match:

enum Fruit {
    Apple,
    Banana,
    Orange,
}

fn fruit_match(fruit: Fruit)  {
	// match 必须列出所有可能;
    match fruit {
        Fruit::Apple => println!("It's an apple!"),
        Fruit::Banana => println!("It's a banana!"),
        Fruit::Orange => println!("It's an orange!"),
    }
}

fn main() {
    let fruit: Fruit = Fruit::Apple;
    fruit_match(fruit);
    let fruit: Fruit = Fruit::Banana;
    fruit_match(fruit);
    let fruit: Fruit = Fruit::Orange;
    fruit_match(fruit);
}
/*输出
It's an apple!
It's a banana!
It's an orange!
*/

五 其他数据类型

除了基本数据类型、复合数据类型和自定义数据类型,Rust还提供了其他一些常用的数据类型。

5.1 切片

切片是对数组或字符串的引用,它允许我们引用集合中的一部分而不用拷贝整个集合。切片使用&符号和范围表示。
以下是一个切片的示例:

// 左闭右开[ )。包含start_index下标位置元素,不包含end_index下标位置的元素
fn main() {
	let numbers: [i32; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
	let slice: &[i32] = &numbers[0..1];//索引从下标 0开始; 
	println!("Slice : {:?}", slice);
	let slice: &[i32] = &numbers[..1];//缺省start_index; 默认从 0 开始
	println!("Slice : {:?}", slice);
	let slice: &[i32] = &numbers[5..10];
	println!("Slice : {:?}", slice);
	let slice: &[i32] = &numbers[5..];//缺省end_index; 默认到最后一个元素截止;
	println!("Slice : {:?}", slice);

	//字符串切片
	let str  = String::from("helloworld");
	let slice = &str[0..5];
	println!("str = {}", slice);
/*输出
Slice : [0]
Slice : [0]
Slice : [5, 6, 7, 8, 9]
Slice : [5, 6, 7, 8, 9] 
str = hello */	
}

5.2 Option类型

Option类型在Rust中用于表示可能为空的值。它有两个可能的取值:Some(value)表示有值,None表示无值。Option类型可以帮助我们处理可能出现空值的情况。
Option是Rust的为了解决函数返回null问题而产生的。
Option 类型是 Rust 标准库中的一个枚举类型,定义如下:

pub enum Option<T> {
    None,
    Some(T),
}
fn divide(x: f64, y: f64) -> Option<f64> {
    if y != 0.0 {
        Some(x / y)
    } else {
        None
    }
}

fn main() {
    let result = divide(10.0, 2.0);

    match result {
        Some(value) => println!("Result: {}", value),
        None => println!("Cannot divide by zero"),
    }
}
//Result: 5

5.3 Result类型

Result类型在Rust中用于处理可能发生错误的操作。它有两个可能的取值:Ok(value)表示操作成功,返回一个值,Err(error)表示操作失败,返回一个错误。
以下是一个Result类型的示例:

fn divide(x: f64, y: f64) -> Result<f64, String> {
    if y != 0.0 {
        Ok(x / y)
    } else {
        Err("Cannot divide by zero".to_string())
    }
}

fn main() {
    let result = divide(10.0, 2.0);

    match result {
        Ok(value) => println!("Result: {}", value),
        Err(error) => println!("Error: {}", error),
    }
}
//Result: 5

总结

本文介绍了Rust的基础语法以及Rust的各种数据类型,包括布尔类型、整数类型、浮点类型、字符类型、元组、数组、字符串、结构体、枚举、切片、Option类型和Result类型。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值