【问题标题】:How to share resources without exposing Rc in the API?如何在 API 中不暴露 Rc 的情况下共享资源?
【发布时间】:2021-11-10 17:25:04
【问题描述】:

我正在研究一个几何板条箱,我想为了让事情正常工作,我应该在引用共享点时使用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

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

【问题讨论】:

  • 需要将它们全部作为共享参考吗?你打算就地改变它们吗?在我看来,Line 拥有两个 Points 的唯一所有权更有意义。根据Vec4 是什么,您甚至可以摆脱Point : Copy,这样移动它们就更加方便了。
  • 这里可能还有其他也不是很明显的假设。例如,用 4 个点,我可以从相同的 4 个顶点制作 6 条线和 4 个三角形。所以这可能很重要,他们的模型可能就是他们需要的。

标签: rust reference-counting


【解决方案1】:

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

保持结构不变,但最好使用表明它们是内部结构的命名方案(通常以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 字段或使用 new type 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)],
                    }),
                ],
            }),
        }
    }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2010-09-14
    • 2019-07-13
    • 2016-05-02
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    相关资源
    最近更新 更多