在上一篇里,定义了一个网格构造
struct Mesh {
points: Vec<Vec<f64>>, // 点表
facets: Vec<Vec<usize>> // 面表
}
有什么理由要求 Mesh
的 points
里的各顶点坐标必须是 f64
类型呢?
没有理由,所以 Mesh
构造应该定义为
struct Mesh<T> {
n: usize, // 维度
points: Vec<Vec<T>>, // 点表
facets: Vec<Vec<usize>> // 面表
}
请留神,我还为 Mesh
减少了维度信息。至于 facets
,因为它存储的是顶点在 points
里的下标,有理由要求它是 usize
类型,因为 Vec
的下标就是 usize
类型。
一个向量,其元素为向量,该向量便是矩阵,因而 Mesh
的 points
成员实际上是矩阵,同理,Mesh
的 facets
成员也是矩阵,故而在上一篇里,定义了泛型函数 matrix_fmt
将二者转化为字符串(String
类型)。当初,因为 Mesh
的定义产生了变动,matrix_fmt
也要相应有所变动,借此可再稍微复习一遍泛型。
首先,写出一个兴许并不正确的 matrix_fmt
:
struct Prefix<T> {
status: bool,
body: fn(&T) -> String
}
impl<T> Prefix<T> {fn new() -> Prefix<T> {Prefix{status: false, body: |_| "".to_string()}
}
}
fn matrix_fmt<T>(v: &Vec<T>, prefix: Prefix<T>) -> String {let mut s = String::new();
for x in v {let n = x.len();
if prefix.status {s += (prefix.body)(x).as_str();}
for i in 0 .. n {
if i == n - 1 {s += format!("{}\n", x[i]).as_str();} else {s += format!("{}", x[i]).as_str();}
}
}
return s;
}
通过 rustc 的一番调教,matrix_fmt
最终会变成
fn matrix_fmt<T: Length + Index<usize>>(v: &Vec<T>,
prefix: Prefix<T>) -> String
where <T as Index<usize>>::Output: fmt::Display,
<T as Index<usize>>::Output: Sized {let mut s = String::new();
for x in v {let n = x.len();
if prefix.status {s += (prefix.body)(x).as_str();}
for i in 0 .. n {
if i == n - 1 {s += format!("{}\n", x[i]).as_str();} else {s += format!("{}", x[i]).as_str();}
}
}
return s;
}
之所以为 T
减少 fmt::Display
束缚,是因为代码中 format!
的参数是 T
。之所以为 T
减少 Sized
束缚,是因为 rustc 心愿可能在编译期间确定 T
的实例占用多少字节的空间。
基于 matrix_fmt
便可为 Mesh
实现 Display
Trait:
impl<T: fmt::Display> fmt::Display for Mesh<T> {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {let mut info = String::new();
info += format!("OFF\n").as_str();
info += format!("{0} {1} 0\n", self.points.len(), self.facets.len()).as_str();
info += matrix_fmt(&self.points, Prefix::new()).as_str();
info += matrix_fmt(&self.facets, Prefix{status: true,
body: |x| format!("{}", x.len())}).as_str();
write!(f, "{}", info)
}
}
残缺的代码如下:
use std::fmt;
use std::path::Path;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::str::FromStr;
use std::num::ParseFloatError;
use std::ops::Index;
struct Mesh<T> {
n: usize, // 维度
points: Vec<Vec<T>>, // 点表
facets: Vec<Vec<usize>> // 面表
}
impl<T: FromStr<Err = ParseFloatError>> Mesh<T> {fn new(n: usize) -> Mesh<T> {return Mesh {n: n, points: Vec::new(), facets: Vec::new()};
}
fn load(&mut self, path: &str) {let path = Path::new(path);
let file = File::open(path).unwrap();
let buf = BufReader::new(file);
let mut lines_iter = buf.lines().map(|l| l.unwrap());
assert_eq!(lines_iter.next(), Some(String::from("OFF")));
let second_line = lines_iter.next().unwrap();
let mut split = second_line.split_whitespace();
let n_of_points: usize = split.next().unwrap().parse().unwrap();
let n_of_facets: usize = split.next().unwrap().parse().unwrap();
for _i in 0 .. n_of_points {let line = lines_iter.next().unwrap();
let mut p: Vec<T> = Vec::new();
for x in line.split_whitespace() {p.push(x.parse().unwrap());
}
self.points.push(p);
}
for _i in 0 .. n_of_facets {let line = lines_iter.next().unwrap();
let mut f: Vec<usize> = Vec::new();
let mut split = line.split_whitespace();
let n:usize = split.next().unwrap().parse().unwrap();
assert_eq!(n, self.n);
for x in split {f.push(x.parse().unwrap());
}
assert_eq!(n, f.len());
self.facets.push(f);
}
}
}
trait Length {fn len(&self) -> usize;
}
impl<T> Length for Vec<T> {fn len(&self) -> usize {return self.len();
}
}
struct Prefix<T> {
status: bool,
body: fn(&T) -> String
}
impl<T> Prefix<T> {fn new() -> Prefix<T> {Prefix{status: false, body: |_| "".to_string()}
}
}
fn matrix_fmt<T: Length + Index<usize>>(v: &Vec<T>,
prefix: Prefix<T>) -> String
where <T as Index<usize>>::Output: fmt::Display {let mut s = String::new();
for x in v {let n = x.len();
if prefix.status {s += (prefix.body)(x).as_str();}
for i in 0 .. n {
if i == n - 1 {s += format!("{}\n", x[i]).as_str();} else {s += format!("{}", x[i]).as_str();}
}
}
return s;
}
impl<T: fmt::Display> fmt::Display for Mesh<T> {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {let mut info = String::new();
info += format!("OFF\n").as_str();
info += format!("{0} {1} 0\n", self.points.len(), self.facets.len()).as_str();
info += matrix_fmt(&self.points, Prefix::new()).as_str();
info += matrix_fmt(&self.facets, Prefix{status: true,
body: |x| format!("{}", x.len())}).as_str();
write!(f, "{}", info)
}
}
fn main() {let mut mesh: Mesh<f64> = Mesh::new(3);
mesh.load("foo.off");
print!("{}", mesh);
}