乐趣区

关于rust:与-Rust-勾心斗角点

上一篇:前言

三维世界的原点,就是在三个数轴上的投影为 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.00 不同。

当初能够写一个可能问候原点的程序了,

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 对我敌对了许多。

退出移动版