首页 > 解决方案 > 如何在 API 中不暴露 Rc 的情况下共享资源?

问题描述

我正在研究一个几何板条箱,我想为了让事情正常工作,我应该Rc在引用共享点时使用。例如:

struct Point {
    point: Vec4,
}

struct Line {
    points: [Rc<Point>; 2],
}

struct Tri {
    points: [Weak<Point>; 3],
    edges: [Rc<Line>; 3],
}

struct Tetra {
    points: [Weak<Point>; 4],
    edges: [Weak<Line>; 6],
    faces: [Rc<Tri>; 4],
}

这是有道理的,因为它似乎是跟踪单纯形组件的最有效方式。问题出在 API 中。我不想暴露这个Rc内部,如果我可以传入引用,我会喜欢它。例如,像这样的东西:

let points = [Point::new(p0), Point::new(p1), Point::new(p2), Point::new(p3)];
let tris = [
    Tri::new(&points[0], &points[1], &points[2]),
    Tri::new(&points[2], &points[3], &points[0])
];

等等,无需用户明确处理Rc自己。

我们通常在这里看到什么样的模式?

标签: rustreference-counting

解决方案


如果您希望在没有用户Rc自己管理 s 的情况下共享资源,您可以照常创建内部结构,并Rc为您的公共接口创建围绕 s 的包装器。

保持结构不变,但最好使用表明它们是内部的(通常InnerImpl后缀)的命名方案:

struct PointInner {
    point: Vec4,
}

struct LineInner {
    points: [Rc<PointInner>; 2],
}

struct TriInner {
    points: [Weak<PointInner>; 3],
    edges: [Rc<LineInner>; 3],
}

struct TetraInner {
    points: [Weak<PointInner>; 4],
    edges: [Weak<LineInner>; 6],
    faces: [Rc<TriInner>; 4],
}

然后创建您的面向用户的类型,它们只是Rcs 周围的包装器,具有处理内部链接的漂亮外观(您可以inner像我所做的那样使用字段或使用新类型 structs):

pub struct Point {
    inner: Rc<PointInner>,
}

pub struct Line {
    inner: Rc<LineInner>,
}

pub struct Tri {
    inner: Rc<TriInner>,
}

pub struct Tetra {
    inner: Rc<TetraInner>,
}

impl Point {
    pub fn new(point: Vec4) -> Point {
        Point {
            inner: Rc::new(PointInner { point }),
        }
    }
}

impl Tri {
    pub fn new(p1: &Point, p2: &Point, p3: &Point) -> Tri {
        Tri {
            inner: Rc::new(TriInner {
                points: [
                    Rc::downgrade(&p1.inner),
                    Rc::downgrade(&p2.inner),
                    Rc::downgrade(&p3.inner),
                ],
                edges: [
                    Rc::new(LineInner {
                        points: [Rc::clone(&p1.inner), Rc::clone(&p2.inner)],
                    }),
                    Rc::new(LineInner {
                        points: [Rc::clone(&p2.inner), Rc::clone(&p3.inner)],
                    }),
                    Rc::new(LineInner {
                        points: [Rc::clone(&p3.inner), Rc::clone(&p1.inner)],
                    }),
                ],
            }),
        }
    }
}

有了这个,您想要的代码无需更改即可工作。看到它在操场上工作。


推荐阅读