Rust学习(通过例子学习Rust)(2)
九. 函数
使用fn 关键字开头
9.1 方法
方法(method)是依附于对象的函数。这些方法通过关键字 self 来访问对象中的数据和其他。方法在 impl 代码块中定义。
struct Point {
x: f64,
y: f64,
}
impl Point {
//这类方法一般用 constructor
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
//
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
struct Rectangle {
p1: Point,
p2: Point,
}
impl Rectangle {
fn area(&self) -> f64 {
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
((x1 - x2) * (y1 - y2)).abs()
}
fn perimeter(&self) -> f64 {
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
}
fn translate(&mut self, x: f64, y: f64) {
self.p1.x += x;
self.p1.y += y;
self.p2.x += x;
self.p2.y += y;
}
}
fn main() {
let rectangle = Rectangle {
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
println!("Area: {}", rectangle.area()); //12
// `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
println!("Perimeter: {}", rectangle.perimeter()); //14
// 移动矩形
let mut rectangle = Rectangle {
p1: Point::origin(),
p2: Point::new(1.0, 1.0),
};
rectangle.translate(1.0, 1.0);
println!("Area: {}", rectangle.area()); //1
}
9.2 闭包
9.2.1 闭包的定义
Rust 中的闭包(closure),也叫做 lambda 表达式或者 lambda,是一类能够捕获周围作用域中变量的函数
fn main() {
// 通过闭包和函数实现自增
//注意结尾没有分号
fn function(i: u32) -> u32 {
i + 1
}
let fna = |i: u32| -> u32 { i + 1 };
let fnb = |i| i + 1;
let i = 1;
println!("{}", function(i));
println!("{}", fna(i));
println!("{}", fnb(i));
println!("hello world!");
}
9.2.2 闭包的捕获 和作为输入参数
通过引用:&T;
通过可变引用:&mut T;
通过值:T;
Fn:表示捕获方式为通过引用(&T)的闭包
FnMut:表示捕获方式为通过可变引用(&mut T)的闭包
FnOnce:表示捕获方式为通过值(T)的闭包
fn main() {
use std::mem;
//Fn()
let color = String::from("green");
let p = || println!("{}", color);
p(); //greem
//FnMut()
let mut count = 0;
let mut inc = || {
count += 1;
println!("count: {}", count);
};
inc(); //count: 1
inc(); //count: 2
let movalbe = Box::new(3);
let consume = || {
println!("`movalbe`:{:?}", movalbe);
mem::drop(movalbe); //move
};
consume();
//error
//consume();
}
9.2.3 类型匿名
fn main() {
限定了 fn 类型
fn apply<F: Fn()>(f: F) {
f();
}
let f = || println!("hello");
apply(f);
}
9.2.4 输入参数;
fn call_me<F>(f: F)
where
F: Fn(),
{
f();
}
fn function() {
println!("function");
}
fn main() {
let c = || println!("c");
call_me(c);
call_me(function);
}
9.2.5 输出参数
返回闭包的有效特征是:
Fn
FnMut
FnOnce
fn create_fn() -> impl Fn() {
let text = "Fn".to_owned();
move || println!("{}", text)
}
fn create_fnmut() -> impl FnMut() {
let text = "FnMut".to_owned();
move || println!("{}", text)
}
fn create_fnonce() -> impl FnOnce() {
let text = "FnOnce".to_owned();
move || println!("{}", text)
}
fn main() {
let fn_once = create_fnonce();
fn_once();
let mut fn_mut = create_fnmut();
fn_mut();
let fn1 = create_fn();
fn1();
}
9.3 高阶函数
高阶函数是函数式编程的特性,它是指函数可以作为参数传递给另一个函数,或者返回一个函数作为另一个函数的返回值
Rust 提供了高阶函数(Higher Order Function, HOF),指那些输入一个或多个函数,并且/或者产生一个更有用的函数的函数
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
fn main() {
let u = 1000;
let mut acc = 0;
for n in 0.. {
let n_squared = n * n;
if n_squared > u {
break;
} else {
acc += n_squared;
}
}
println!("{}", acc);
let sum = (0..)
.map(|n| n * n)
.take_while(|&n| n < 1000)
.filter(|&n| is_odd(n))
.fold(0, |sum, i| sum + i);
println!("sum = {}", sum);
}
9.4 发散函数 (!)
发散函数(diverging function)绝不会返回。 它们使用 ! 标记,这是一个空类型。
fn foo() -> ! {
panic!("foo");
}
fn main() {
foo();//返回空
}
fn main() {
fn sum_odd_numbers(up_to: u32) -> u32 {
let mut acc = 0;
for i in 0..up_to {
// 注意这个 match 表达式的返回值必须为 u32,
// 因为 “addition” 变量是这个类型。
let addition: u32 = match i%2 == 1 {
// “i” 变量的类型为 u32,这毫无问题。
true => i,
// 另一方面,“continue” 表达式不返回 u32,但它仍然没有问题,
// 因为它永远不会返回,因此不会违反匹配表达式的类型要求。
false => continue,
};
acc += addition;
}
acc
}
println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
}
10. 模块
Rust 提供了一套强大的模块(module)系统,可以将代码按层次分成多个逻辑单元(模块),并管理这些模块之间的可见性(公有(public)或私有(private))。
模块是项(item)的集合,项可以是:函数,结构体,trait,impl 块,甚至其它模块。
10.1 模块的可见性
默认情况下,模块中的项拥有私有的可见性(private visibility),不过可以加上 pub
修饰语来重载这一行为。模块中只有公有的(public)项可以从模块外的作用域访问
10.2 结构体的可见性
结构体的字段也是一个可见性的层次。字段默认拥有私有的可见性,也可以加上 pub 修饰语来重载该行为。只有从结构体被定义的模块之外访问其字段时,这个可见性才会起作用,其意义是隐藏信息(即封装,encapsulation)
mod my {
#[derive(Debug)]
pub struct OpenBox<T> {
pub content: T,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct ClosedBox<T> {
content: T,
}
impl<T> ClosedBox<T> {
pub fn new(content: T) -> Self {
ClosedBox { content }
}
}
}
fn main() {
let open_box = my::OpenBox { content: 10 };
let close_box = my::ClosedBox::new(10);
println!("{:?}", open_box);
println!("{:?}", close_box);
println!("{:?}", open_box.content);
// error field `content` of `CloseBox` is private
//println!("{:?}", close_box.content);
// error field `content` of `CloseBox` is private
//let close_box2 = my::ClosedBox { content: 10 };
}
10.3 use 声明
use 声明可以将一个完整的路径绑定到一个新的名字,从而更容易访问。
10.4 super和self
在模块中,super 关键字表示当前模块的父模块,self 关键字则表示当前模块。
fn function() {
println!("call funciton");
}
mod cool {
pub fn function() {
println!("call cool function");
}
}
mod my {
fn funciton() {
println!("call my function");
}
mod cool {
pub fn function() {
println!("call cool function");
}
}
pub fn indirect_call() {
println!("call function start");
//self
//slef::function() 和function() 一样
self::funciton();
funciton();
//self::my
self::cool::function();
//super::function ·my· 模块同级的function
super::function();
//最外层 cool::function
{
use crate::cool::function;
function();
}
}
}
10.5 文件分层
$ tree .
.
|-- my
| |-- inaccessible.rs
| |-- mod.rs
| `-- nested.rs
`-- split.rs
//split.rs
mod my;
fn function(){
println!("call function");
}
fn main() {
my::funciton();
function();
my::indirect_call();
my::nested::function();
}
//mod.rs
pub mod nested;
mod inaccessible;
pub fn funciton(){
println!("call funciton");
}
fn private_function(){
println!("call private function");
}
pub fn indirect_call() {
println!("call function start");
private_function();
}
//nested.rs
pub fn function() {
println!("call nested function");
}
fn private_function(){
println!("call private function");
}
//inaccessible.rs
pub fn public_function(){
println!("call public function");
}
十一. create
crate(中文有 “包,包装箱” 之意)是 Rust 的编译单元
编译库
rustc --create-type=lib mylib.rs
使用库
ructs main.rs --extern mylib=library.rlib
十二. cargo
/// 1. 创建一个新项目
cargo new hello
/// 2. 创建一个库
cargo new --lib hello
// 项目目录
foo
├── Cargo.toml
└── src
├── main.rs
└── bin
└── my_other_bin.rs
/// 3.测试
cargo test
/// 4. 构建
[package]
…
build = “build.rs”
十三. 属性
/// 1. 属性语法
#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value)]
#[attribute(value, value2)]
/// 2. 死代码
#[allow(dead_code)]
/// 3. create
/// 4.cfg 宏
cfg 属性:在属性位置中使用 #[cfg(…)]
cfg! 宏:在布尔表达式中使用 cfg!(…)
有部分条件如 target_os 是由 rustc 隐式地提供的,但是自定义条件必须使用 --cfg 标记来传给 rustc。
十四. 范型
泛型的类型参数是使用尖括号和大驼峰命名的名称:<Aaa, Bbb, …> 来指定的。泛型类型参数一般用 来表示。在 Rust 中,“泛型的” 除了表示类型,还表示可以接受一个或多个泛型类型参数 的任何内容。任何用泛型类型参数表示的类型都是泛型,其他的类型都是具体(非泛型)类型。
14.1. 泛型函数
```struct A; // 具体类型 A
。
struct S(A); // 具体类型 S
。
struct SGen(T); // 泛型类型 SGen
。
// 下面全部函数都得到了变量的所有权,并立即使之离开作用域,将变量释放。
// 定义一个函数 reg_fn
,接受一个 S
类型的参数 _s
。
// 因为没有 <T>
这样的泛型类型参数,所以这不是泛型函数。
fn reg_fn(_s: S) {}
// 定义一个函数 gen_spec_t
,接受一个 SGen<A>
类型的参数 _s
。
// SGen<>
显式地接受了类型参数 A
,且在 gen_spec_t
中,A
没有被用作
// 泛型类型参数,所以函数不是泛型的。
fn gen_spec_t(_s: SGen) {}
// 定义一个函数 gen_spec_i32
,接受一个 SGen<i32>
类型的参数 _s
。
// SGen<>
显式地接受了类型参量 i32
,而 i32
是一个具体类型。
// 由于 i32
不是一个泛型类型,所以这个函数也不是泛型的。
fn gen_spec_i32(_s: SGen) {}
// 定义一个函数 generic
,接受一个 SGen<T>
类型的参数 _s
。
// 因为 SGen<T>
之前有 <T>
,所以这个函数是关于 T
的泛型函数。
fn generic(_s: SGen) {}
fn main() {
// 使用非泛型函数
reg_fn(S(A)); // 具体类型。
gen_spec_t(SGen(A)); // 隐式地指定类型参数 A
。
gen_spec_i32(SGen(6)); // 隐式地指定类型参数 i32
。
// 为 `generic()` 显式地指定类型参数 `char`。
generic::<char>(SGen('a'));
// 为 `generic()` 隐式地指定类型参数 `char`。
generic(SGen('c'));
}
#### 14.2. 泛型实现
struct Val {
val: f64
}
struct GenVal{
gen_val: T
}
// Val 的 impl
impl Val {
fn value(&self) -> &f64 { &self.val }
}
// GenVal 的 impl
,指定 T
是泛型类型
impl GenVal {
fn value(&self) -> &T { &self.gen_val }
}
fn main() {
let x = Val { val: 3.0 };
let y = GenVal { gen_val: 3i32 };
println!("{}, {}", x.value(), y.value());
}
#### 14.3. trait
当然 trait 也可以是泛型的
#### 14.4. 泛型约束
在使用泛型时,类型参数常常必须使用 trait 作为约束(bound)来明确规定类型应实现哪些功能
#### 14.5. 多从约束
多重约束(multiple bounds)可以用 + 连接。和平常一样,类型之间使用 , 隔开。
use std::fmt::{Debug, Display};
fn compare_prints<T: Debug + Display>(t: &T) {
println!(“Debug: {:?}
”, t);
println!(“Display: {}
”, t);
}
fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
println!(“t: {:?}
”, t);
println!(“u: {:?}
”, u);
}
fn main() {
let string = “words”;
let array = [1, 2, 3];
let vec = vec![1, 2, 3];
compare_prints(&string);
//compare_prints(&array);
// 试一试 ^ 将此行注释去掉。
compare_types(&array, &vec);
}
#### 14.6 where 子句
约束也可以使用 where 分句来表达,它放在 { 的前面,而不需写在类型第一次出现之前。另外 where 从句可以用于任意类型的限定,而不局限于类型参数本身。
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}
// 使用 `where` 从句来表达约束
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB + TraitC,
D: TraitE + TraitF {}
#### 14.7 newtype
定义字段机构
#### 14.8 关联项
关联项”(associated item)指与多种类型的项有关的一组规则。它是 trait 泛型的扩展,允许在 trait 内部定义新的项。
struct Container(i32, i32);
trait Contans {
// 在这里定义可以被方法使用的泛型类型。
type A;
type B;
fn contains(&self, a: &Self::A, b: &Self::B) -> bool;
fn first(&self) -> i32;
fn second(&self) -> i32;
}
impl Contans for Container {
//指出类型 实现泛型类型
type A = i32;
type B = i32;
fn contains(&self, a: &Self::A, b: &Self::B) -> bool {
a == b
}
fn first(&self) -> Self::A {
self.0
}
fn second(&self) -> Self::B {
self.1
}
}
fn difference(container: Container) -> i32 {
container.first() - container.second()
}
fn main() {
let numbrer1 = 2;
let numbber2 = 10;
let container = Container(numbrer1, numbber2);
println!(“{}”, difference(container));
}
#### 14.9 虚类型参数
虚类型(phantom type)参数是一种在运行时不出现,而在(且仅在)编译时进行静态检查的类型参数。
#[derive(PartialEq)] // 允许这种类型进行相等测试。