與 Rust 勾心鬥角 · 泛得徹底點兒

garfileo發表於2022-05-05

在上一篇裡,定義了一個網格結構

struct Mesh {
    points: Vec<Vec<f64>>,  // 點表
    facets: Vec<Vec<usize>> // 面表
}

有什麼理由要求 Meshpoints 裡的各頂點座標必須是 f64 型別呢?

沒有理由,所以 Mesh 結構應該定義為

struct Mesh<T> {
    n: usize, // 維度
    points: Vec<Vec<T>>,  // 點表
    facets: Vec<Vec<usize>> // 面表
}

請注意,我還為 Mesh 增加了維度資訊。至於 facets,由於它儲存的是頂點在 points 裡的下標,有理由要求它是 usize 型別,因為 Vec 的下標就是 usize 型別。

一個向量,其元素為向量,該向量便是矩陣,因此 Meshpoints 成員實際上是矩陣,同理,Meshfacets 成員也是矩陣,故而在上一篇裡,定義了泛型函式 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);
}

相關文章