上一篇:前言
三维世界的原点,就是在三个数轴上的投影为 0 的点。假如三个数轴为 x,y 和 z,则原点在它们上的投影可示意为 x = 0, y = 0, z = 0,用 Rust 代码可示意为
let x: f64 = 0.0;
let y: f64 = 0.0;
let z: f64 = 0.0;
亦即定义了三个变量 x, y, z,它们皆为 64 位的浮点类型,值皆为浮点类型的字面量 0.0。留神,0.0
和 0
不同。
当初能够写一个可能问候原点的程序了,
fn main() {
let x: f64 = 0.0;
let y: f64 = 0.0;
let z: f64 = 0.0;
println!("你好啊,({0}, {1}, {2})!", x, y, z);
}
编译,运行:
$ rustc foo.rs
$ ./foo
你好啊,(0, 0, 0)!
构造体
应用构造体类型可将 x, y, z 绑定到一起,结构形象意义的三维原点,例如:
struct Point {x: f64, y: f64, z: f64}
fn main() {let origin: Point = Point {x: 0.0, y: 0.0, z: 0.0};
println!("你好啊,({0}, {1}, {2})!", origin.x, origin.y, origin.z);
}
因为 rustc 可能依据值的语法模式推断出变量类型,因而
let origin: Point = Point {x: 0.0, y: 0.0, z: 0.0};
可简化为
let origin = Point {x: 0.0, y: 0.0, z: 0.0};
办法
Rust 语言容许为构造体类型定义办法——有些非凡的函数,例如:
impl Point {fn origin() -> Point {Point {x: 0.0, y: 0.0, z: 0.0}
}
}
应用上述定义的办法,可进一步简化结构原点的语句:
let origin = Point::origin();
origin
办法是通过类型 Point
调用,这类办法称为「静态方法(Static Method)」。也能够为构造体类型的实例定义方法,这类办法称为「实例办法(Instance Method)」,例如:
impl Point {fn hello(&self) {println!("你好啊,我是 ({0}, {1}, {2}!", self.x, self.y, self.z);
}
}
以下代码结构一个点的实例,并调用实例办法 hello
:
let x = Point::origin();
x.hello();
n 维点
若点的维度任意,用 C 语言,可将其定义为
typedef struct {
size_t n;
double *body;
} Point;
body
指向堆空间里大小为 n * sizeof(double)
的一段空间。用 Rust 语言如何定义相似的构造体?可应用 Box<T>
指针,例如
struct Point {
n: isize,
body: Box<[f64]>
}
在 Rust 语言里,Box<T>
是泛型的智能指针。在上例中,T
即 [f64]
,即成员类型为 f64
的数组类型。
为 C 语言版本的 Point
类型构建一个实例:
size_t n = 3;
double *body = malloc(n * sizeof(double));
body[0] = 0.1; body[1] = 0.2; body[2] = 0.3;
Point x = (Point){.n = n, .body = body};
printf("你好啊,%zu 维点 (%f, %f, %f)!\n", x.n, x.body[0], x.body[1], x.body[2]);
free(body);
相似地,上述 Rust 语言版本的 Point
的实例化过程为
let x = Point {n: 3, body: Box::new([0.1, 0.2, 0.3])};
println!("你好啊,{0} 维点 ({1}, {2}, {3})!", x.n, x.body[0], x.body[1], x.body[2]);
在上述示例里,Rust 语言要比 C 语言简洁得多。另外,无论是 C 语言还是 Rust 语言,示例中的 x.body
所指向的内存空间位于程序的堆空间,然而前者须要显示回收,而后者可主动回收,故 Box<T>
称为「智能指针」。
数组和向量
与晚期的 C 语言相似,Rust 语言里的数组是固定长度的类型,亦即在定义数组实例时须要指定数组的长度,例如
let x: [f64; 3] = [0.1, 0.2, 0.3];
C 语言自 C99 规范开始反对变长数组。不过,因为数组空间位于栈上,对于大量的数据而言,数组是否变长并不重要,因为通常须要在堆空间结构数组。堆空间的内存能够由编程者自行调配和保护,因而实现变长数组仅仅是算法问题,而不是语法问题。
须要留神的是,在 Rust 语言里,若在堆空间为数组调配空间,所用的智能指针 Box<T>
的泛型参数 T
的值尽管作为数组类型,然而不须要提供数组长度,只需提供数组元素的类型,例如 [f64]
。
Rust 语言提供了堆空间变长数组的实现,即向量(Vec
)类型,可间接基于该类型定义 n 维点,例如
let mut x: Vec<f64> = Vec::new();
x.push(0.1); x.push(0.2); x.push(0.3);
println!("你好啊,{0} 维点 ({1}, {2}, {3})!", x.len(), x[0], x[1], x[2]);
用 Vec::new
结构的向量实例,若向其中减少元素,则须要将向量实例设定为可变,即 mut
。
应用 vec!
可将上述代码简化为
let x: Vec<f64> = vec![0.1, 0.2, 0.3];
println!("你好啊,{0} 维点 ({1}, {2}, {3})!", x.len(), x[0], x[1], x[2]);
vec!
可在堆空间结构一个向量空间,而后将栈空间里的数组的数据复制到向量空间,若后续不须要批改向量的内容或向其中减少新元素,不须要将向量实例设为可变。
上一节基于 Box<[f64]>
定义的 n 维点,实际上相当于低配版本的 Vec<f64>
,如无必要,通常应该应用后者,而且能够应用类型别名的模式,例如
type Point = Vec<f64>;
个性
上面是一个残缺的程序,它可能让一个 n 维点自报家门:
type Point = Vec<f64>;
fn hello(x: &Point) {let n = x.len();
print!("你好啊,我是 {} 维点 (", x.len());
for i in 0 .. n {if i != (n - 1) {print!("{},", x[i]);
} else {print!("{}", x[i]);
}
}
println!(")!");
}
fn main() {let x: Point = vec![0.1, 0.2, 0.3];
hello(&x);
}
程序的输入后果为
你好啊,我是 3 维点 (0.1, 0.2, 0.3)!
是否将 hello
作为 Point
亦即 Vec<f64>
实例的办法呢?例如
impl Point {fn hello(&self) {let n = self.len();
print!("你好啊,我是 {} 维点 (", self.len());
for i in 0 .. n {if i != (n - 1) {print!("{},", self[i]);
} else {print!("{}", self[i]);
}
}
println!(")!");
}
}
rustc 说不行。错误信息为
error[E0116]: cannot define inherent `impl` for a type outside of the crate
where the type is defined
而后给出批改倡议:
define and implement a trait or new type instead
上面定义一个 Hello
个性(Trait)试试,
trait Hello {fn hello(&self);
}
而后为 Point
亦即 Vec<f64>
类型实现 Hello
个性,
impl Hello for Point {fn hello(&self) {let n = self.len();
print!("你好啊,我是 {} 维点 (", self.len());
for i in 0 .. n {if i != (n - 1) {print!("{},", self[i]);
} else {print!("{}", self[i]);
}
}
println!(")!");
}
}
而后应用 Hello
个性里的 hello
函数,在行为上与类型实例的办法完全一致,例如
fn main() {let x: Point = vec![0.1, 0.2, 0.3];
x.hello();}
尽管个性与办法有相似之处,然而表白的语义不同。办法面向特定类型,而个性面向不同的类型。不同的类型,能够有类似的行为。人要吃货色, 睡觉, 生孩子。动物不是人, 也要吃货色, 睡觉, 生孩子。路线上,能够走人,也能够行车。为不同的类型结构类似的行为,这就是个性存在的意义。
小结
简直将一年前学过的一点 Rust 语言温习了一遍,只有 Box<T>
指针是初学,最大的感触是,rustc 对我敌对了许多。