rust 在编译时关联值

wqsoz72f  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(149)

我想制作一个具有安全接口的图形:

pub struct VertexId {
  id: usize,
}

pub struct Graph {
  vertices: Vec<String>,
  edges: Vec<(VertexId, VertexId)>,
}

impl Graph {
  pub fn add_vertex(&mut self, label: String) -> VertexId {
    self.vertices.push(label);
    VertexId { id: self.vertices.len() - 1 }
  }
  pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
    self.edges.push((from, to));
  }
}

在这里,我创建了一个VertexId Package 器,以便您只能从Graph中获取顶点id。
但是,如果创建两个Graph,则可能使用无效的VertexId:

let mut a = Graph::new();
  let vid = a.add_vertex("hello".to_string());

  let mut b = Graph::new();
  b.add_edge(vid, vid);

是否可以在编译时将vid链接到a

fdbelqdn

fdbelqdn1#

  • 注意:* 如果您决定使用下面的方法,您会发现它会带来严重的缺点,并且-在实践中-可能不值得。

你可以使每个图示例成为一个自己的类型,一种方法是给它配备一个标识符,并要求顶点的标识符和图的标识符匹配。
例如,你可以执行this(我选择IDENTIFIER作为const u32,但是你也可以使用一个类型):

pub struct VertexId<const IDENTIFIER: u32> {
    id: usize,
}

pub struct Graph<const IDENTIFIER: u32> {
    vertices: Vec<String>,
    edges: Vec<(VertexId<IDENTIFIER>, VertexId<IDENTIFIER>)>,
}

impl<const IDENTIFIER: u32> Graph<IDENTIFIER> {
    pub fn add_vertex(&mut self, label: String) -> VertexId<IDENTIFIER> {
        self.vertices.push(label);
        VertexId {
            id: self.vertices.len() - 1,
        }
    }
    pub fn add_edge(&mut self, from: VertexId<IDENTIFIER>, to: VertexId<IDENTIFIER>) {
        self.edges.push((from, to));
    }
}

然后,每次构造一个图时,你都必须提供一个标识符,这很快就会变得很烦人,所以你可以定义这个帮助宏,它可以从行号中推断出标识符:

macro_rules! make_graph {
    () => {{
        const LINE: u32 = line!();
        Graph::<LINE> {
            vertices: Vec::new(),
            edges: Vec::new(),
        }
    }};
}

那么,你就不能将add_edge的顶点从a转移到另一个图b

let mut a = make_graph!();
let vid = a.add_vertex("hello".to_string());

let mut b = make_graph!();
// b.add_edge(vid, vid); // does not compile

注意,make_graph可能导致两个不同文件中的类型相同,但行号匹配。这也突出了它的一个缺点:一旦你把make_graph移到另一行,你就会得到另一个类型。你可以通过使用类型作为标识符来解决这个问题,但是你必须一遍又一遍地声明标识符类型。**tl;dr;**仅仅因为可以在类型系统中强制执行某些内容,这并不一定是一个好主意。

相关问题