Rust 入门笔记【三】
结构体
这是一个结构体的简单例子。
struct Rectangle {
width: u32,
height: u32,
}
fn area(rect: &Rectangle) -> u32 {
rect.width * rect.height
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("{}",area(&rect1));
}
下面这种情况,类似移动构造
struct User {
name: String,
active: bool,
email: String,
sign_in_count: u64,
}
fn main() {
let user1 = User {
name: String::from("alice"),
active: true,
email: String::from("eamil@example.com"),
sign_in_count: 0,
};
let user2 = User {
name: String::from("Bob"),
..user1
};
println!("{}", user1.name);
println!("{}", user2.name);
println!("{}", user1.email);
println!("{}", user2.email);
}
这里创建了两个对象,其中user2只有name与user1不同。但是要注意的是user1中的其他变量都被移动到了user2中,因此如果尝试访问其他变量就会出错。如果我们需要拷贝可以这样写。
let user2 = User {
name: String::from("Bob"),
email:user1.email.clone(),
..user1
};
成员函数
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("{}", rect1.area());
}
这样就是一个成员函数的例子,如果有成员函数中还要传入其他的对象可以这样写
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
println!("{}", rect1.can_hold(&rect2));
println!("{}", rect2.can_hold(&rect1));
}
关联函数
所有在impl块中定义的函数都被称为关联函数,但并不一定要关联self,比如String::from。在rust中是没有默认构造函数的,我们可以自己实现一个关联函数来当作构造函数使用,这样的函数一般命名为new。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
fn main() {
let rect1 = Rectangle::square(10);
let rect2 = Rectangle::new(30, 40);
}
其中Self指代的是impl后的类名,因此我们之前用到的&self实际上是self : &Self的缩写。
一个结构体可以有多个impl块,并且多个impl块之间可以相互访问。
枚举类型
enum IpAddrKind {
v4,
v6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
fn main() {
let home = IpAddr {
kind: IpAddrKind::v4,
address: String::from("127.0.0.1"),
};
}
还有一中方法是我们可以把数据也存储到枚举类型当中
enum IpAddr {
v4(u8, u8, u8, u8),
v6(String),
}
fn main() {
let home = IpAddr::v4(127, 0, 0, 1);
let loopback = IpAddr::v6(String::from("::1"));
}
match 控制流
enum 也可以像 struct 一样使用impl定义成员函数。但是对于不同的类型,函数的实现肯定是不同的,这里就可以用mathc结构实现
enum IpAddr {
v4(u8, u8, u8, u8),
v6(String),
}
impl IpAddr {
fn display(&self) {
match self {
Self::v4(a, b, c, d) => {
println!("IPv4 : {}.{},{},{}", a, b, c, d)
}
Self::v6(s) => {
println!("IPv6: {}", s)
}
}
}
}
fn main() {
let home = IpAddr::v4(127, 0, 0, 1);
let loopback = IpAddr::v6(String::from("::1"));
home.display();
loopback.display();
}
match只会执行第一个匹配成功的。match不一定只能作用于枚举类型,也可是普通类型。并且如果是普通类型,如果枚举不完,match也提供了一个类似else的语法。
fn main() {
let x = 1;
match x {
1 => {println!("one")},
2 => {println!("two")},
3 => {println!("three")},
_ => {println!("other")},
}
}
if let是match的一个语法糖,下面两种写法是等价的
let y = 2;
match y {
2 => {println!("yes")},
_ => {println!("no")},
}
if let 2 = y{
println!("yes");
} else {
println!("no");
}
这样看if let似乎没有优势,但是对于下面这种情况
let y = 3;
match y {
2 => {println!("yes")},
_ => {},
}
if let 2 = y{
println!("yes");
}
let if的else 可以省略,但match的_却不能省略。
Crate
Crate是rust中代码编译的最小单位。可以分为以下两类
- 二进制项,可以被编译为可执行程序,必须包含一个
main函数作为程序的入口 - 库,没有
main函数,也不能被编译为可执行程序。
包是提供一些列功能的一个或多个crate。一个包至少包含了一个Cargo.toml文件。一个包中至多包含一个库,可以任意个二进制项,但至少包含一个crate。
crate root 是一个源文件,编译器非以它为起点构建一个crate 的根模块。默认src/main.rs是与包名相同的二进制crate的根,src/lib.rs是与包名相同的库crate根。
模块
声明模块,在crate根文件中,用mod garden声明一个模块garden,编译器会按照如下顺序寻找模块的代码:
- 内联,在大括号中,当
mod garden后是大括号,不是分号。 - 在文件
src/garden.rs - 在文件
gerden/mod.rs
声明子模块,在garden.rs中定义mod vegetables,编译器会按照如下顺序寻找模块代码
- 内联
- 在文件
src/garden/vegetables.rs - 在文件
src/garden/vegetables/mod.js
模块路径,当模块是crate的一部分是,可以在隐私规则运行的情况下,在crate的任何一个地方访问,比如vegetables下的Asparagus可以用crate::garden::vegetables::Asparagus访问。
一个模块内的代码默认对父模块私有,如果希望公用可以使用pub mod声明。
use关键字,对于上述的例子,可以用use crate::garden::vegetables::Asparagus;创建一个快捷链接方式,在之后的代码中用Asparagus即可访问。
src/main.rs
pub mod garden;
fn main() {
let plant = crate::garden::vegetables::Asparagus {};
println!("{plant:?}");
}
src/garden/mod.rs
pub mod vegetables;
src/garden/vegetables/mod.rs
#[derive(Debug)]
pub struct Asparagus {}
库
我们可以用cargo --lib testlib创建一个testlib库
我们在src/lib.rs可以写
mod A {
pub mod B {
pub fn f() {
println!("ok");
}
}
}
pub fn fuc() {
A::B::f();
}
为什么在A不需要pub mod,fuc也可以正常调用?因为fuc和A在同一模块下,也就是兄弟模块,隐藏只是对父模块。
mod A {
pub mod B {
pub fn f() {
println!("f ok");
self::g();
}
pub fn g() {
println!("g ok");
}
}
}
在同一个模块下相互调用不用pub,并且可以用self代指当前模块,其中self可以省略。
mod A {
fn g() {
println!("g ok");
}
pub mod B {
pub fn f() {
println!("f ok");
super::g();
}
}
}
父模块也不对子模块私有,可以用super::代指父模块,类似Linux路径中的..。
公有结构体
mod A {
pub struct s {
pub x: i32,
y: i32,
}
impl s {
pub fn new(x: i32, y: i32) -> s {
s { x, y }
}
fn getX(&self) -> i32 {
self.x
}
pub fn getY(&self) -> i32 {
self.y
}
}
}
要注意的是虽然s是pub的,但是s的成员变量、成员函数默认都不是,需要显示的pub。
公有枚举
mod A {
pub enum B{
x,
y,
z,
}
}
对于公有枚举类型来说,枚举成员默认都是公有的。

浙公网安备 33010602011771号