在上一篇裡,定義了一個網格結構
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,
<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;
}
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);
}