上一篇:前言三维世界的原点,就是在三个数轴上的投影为 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
指针,例如struct Point {
n: isize,
body: Box
}
在 Rust 语言里,
Box
是泛型的智能指针。在上例中,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
称为「智能指针」。数组和向量 与早期的 C 语言类似,Rust 语言里的数组是固定长度的类型,亦即在定义数组实例时需要指定数组的长度,例如
let x: [f64;
3] = [0.1, 0.2, 0.3];
C 语言自 C99 标准开始支持变长数组。不过,由于数组空间位于栈上,对于大量的数据而言,数组是否变长并不重要,因为通常需要在堆空间构造数组。堆空间的内存可以由编程者自行分配和维护,因此实现变长数组仅仅是算法问题,而不是语法问题。
需要注意的是,在 Rust 语言里,若在堆空间为数组分配空间,所用的智能指针
Box
的泛型参数 T
的值虽然作为数组类型,但是不需要提供数组长度,只需提供数组元素的类型,例如 [f64]
。Rust 语言提供了堆空间变长数组的实现,即向量(
Vec
)类型,可直接基于该类型定义 n 维点,例如let mut x: Vec = 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 = vec![0.1, 0.2, 0.3];
println!("你好啊,{0} 维点 ({1}, {2}, {3})!", x.len(), x[0], x[1], x[2]);
vec!
可在堆空间构造一个向量空间,然后将栈空间里的数组的数据复制到向量空间,若后续不需要修改向量的内容或向其中增加新元素,不需要将向量实例设为可变。上一节基于
Box
定义的 n 维点,实际上相当于低配版本的 Vec
,如无必要,通常应该使用后者,而且可以使用类型别名的形式,例如type Point = Vec;
特性 下面是一个完整的程序,它能够让一个 n 维点自报家门:
type Point = Vec;
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
实例的方法呢?例如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
类型实现 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 勾心斗角》·点】几乎将一年前学过的一点 Rust 语言复习了一遍,只有
Box
指针是初学,最大的感触是,rustc 对我友好了许多。推荐阅读
- docker|基于Rust-vmm实现Kubernetes运行时
- 开源日报|谷歌牵头呼吁保护开源项目;Firefox 更新后服务器出现 Bug;Rust 1.58.0 发布 | 开源日报
- 不要再说 Rust 过度炒作了
- 极客日报|称钉钉将上线“下班勿扰”功能;苹果发生大规模网络宕机;.NET 7 Preview 2发布|极客头条
- 资讯|C 不再是一种编程语言
- CA周记 2022年的第一课 - Rust
- rust|网红编程语言Rust到底是个什么鬼()
- 编程语言|“竟想替代 C 语言(编程语言Go、Rust、C++ 和 Zig 生产力对比!)
- 资讯|编程语言“鄙视链” +1(亚马逊力捧 Rust,Go 技术负责人连发 14 条推特抵制“拉踩”)