rust结构体、枚举、包和作用域
学习视频:B站
struct 结构体
一旦struct是可变的,里面的字段都是可变的。
demo:
struct User {
username: String,
email: String,
active: bool,
}
fn main() {
let user = User {
username: String::from("lei"),
email: String::from("123@gmail.com"),
active: false,
};
}
struct更新语法
let user2 = User {
active: true,
..user // 表示剩下的都和user有相同的字段
};
Tuple struct
例子
不使用struct:
fn main() {
let w = 3;
let l = 5;
println!("{}",area(w, l));
}
fn area(width: u32, length: u32) -> u32 {
width * length
}
使用Tuple:
fn main() {
let rect = (3, 5);
println!("{}",area(rect));
}
fn area(dim: (u32, u32)) -> u32 {
dim.0 * dim.1
}
使用struct:
把长和宽联系在了一起,比较直观。
struct方法
与函数不同之处:
- 是在struct的上下文中定义
- 第一个参数是self,也就是方法被调用的struct实例
demo:
struct Rectangle {
width: u32,
length: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.length
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.length > other.length
}
}
在impl
里面也可以定义关联函数,不是方法,不以self
作为第一个参数。
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { width: size, length: size }
}
}
// 调用:
let s = Rectangle::square(20);
关联函数通常用于构造器。
impl块可以有多个。
枚举
定义枚举
可以把数据附加到枚举的变体中:
- 不需要额外使用struct
- 每个变体拥有不同的类型及关联的数据量
enum IpAddrKind {
V4(u8,u8,u8,u8),
V6(String),
}
fn main() {
let four = IpAddrKind::V4(127,0,0,1);
let six = IpAddrKind::V6(String::from("::1"));
}
方法
也用impl,类似struct中的方法。
Option枚举:
enum Option<T> {
/// No value.
None,
/// Some value of type `T`.
Some(T),
}
Option<T>
比Null好在哪?
要想使用Option<T>
中的T,必须将它转化为T,避免了假设T不为null
的问题。
match
match匹配必须匹配所有的模式,如果剩下的不想管了,用下划线通配符"_"
可以直接返回,也可花括号多行然后返回,也可匹配值。
enum IpAddrKind {
V4,
V6(String),
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6(String::from("haha"));
println!("{}",ip_type(six));
// haha
// 6
}
fn ip_type(ip_kind: IpAddrKind) -> i32 {
match ip_kind {
IpAddrKind::V4 => 4,
IpAddrKind::V6(str) => {
println!("{}",str);
6
},
}
}
match匹配Option<T>
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
print_option(five);
print_option(six);
print_option(none);
// value: 5
// value: 6
// No value
}
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i+1),
}
}
fn print_option(x: Option<i32>) {
match x {
None => {
println!("No value");
}
Some(i) => {
println!("value: {}",i);
}
}
}
if let
只关心一种匹配,忽略其他匹配。可以和else一起。
let v = Some(2u8);
match v {
Some(0) => println!("three"),
_ => (),
}
if let Some(0) = v {
println!("three");
} else {
println!("others");
}
Package, Crate, Module
看着有点蒙:
crate的作用:
- 将相关功能组合到一个作用域中,以便在项目间进行共享,防止冲突。
- 例如
rand crate
,访问它的功能需要通过它的名字:rand
module:
- 在一个crate内,将代码进行分组
- 增加可读性、复用性
- 控制项目私有性,private,public
demo:
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
路径
- 绝对路径,从
crate root
开始 - 相对路径,从当前模块开始
Rust所有条目默认是私有的。父模块无法访问子模块的私有条目,子模块可以使用所有祖先模块的条目。
pub
:标记公共的
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}
super
访问父级模块中的内容。
fn haha() {}
mod front_of_house {
pub fn add_to_waitlist() {
super::haha();
crate::haha();
}
}
pub
放在struct
前,struct
里面的字段还是默认私有。
pub
放在enum
前,enum
里面的字段默认共有。(enum
本来就是给大家共用的)
use
将路径引入作用域。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
crate::front_of_house::hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
use引入也需要遵守私有性规则。use也可以指定相对路径。
use的习惯用法:
- 函数,指定到父级
- struct,enum,指定到本身
- 不同模块同名条目,指定到父级。
use std::fmt::Result;
use std::io::Result as IoResult;
use引入后在作用域是私有的,外部代码看不到。
use crate::front_of_house::hosting;
想要外部代码也看到:
pub use crate::front_of_house::hosting;
使用外部包
cargo.toml添加依赖包(package)
use引入作用域
cargo build blocking wait for file lock on package cache问题:
如之前的rand例子:生成一个随机数
使用嵌套路径清理大量use条目:
use std::cmp::Ordering;
use std::io;
// 可替换为
use std::{cmp::Ordering, io};
use std::io;
use std::io::Write;
// 可替换为
use std::io::{self, Write};
也可使用通配符,但是通常不用。
将模块拆分为不同的文件
模块定义时,如果模块名后面是分号,而不是代码块:
- Rust会从与模块同名的文件中加载内容。
- 模块树的结构不会发生变化。
写在lib.rs
下面:
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
拆分文件:
//lib.rs
mod front_of_house;
// front_of_house.rs
pub mod hosting;
// front_of_house文件夹下hosting.rs
pub fn add_to_waitlist() {}