2

我在 Rust 中包装了一个 C 库,它的许多函数通过指向结构的指针获取参数,这些结构本身通常具有指向其他结构的指针。为了减少开销,我想提供将 Rust 数据编组到 C 结构中的结果缓存的能力。

以下是 C 库可能期望某些参数的示例:

#[repr(C)]
struct Foo {
    x: i32,
    y: f32
}

#[repr(C)]
struct Bar {
    p_foo: *const Foo,
    z: bool
}

我想象一个拥有的“缓存”版本会是什么样子:

struct Cached {
    foo: Option<Foo>,
    bar: Bar
}

p_foo字段bar将被构造为指向 内的Somefoo,或者如果存在则为空指针None

当然,这里的问题是,如果Cached要移动 的值,则顺子memcpy将不合适,并且bar.p_foo还需要重定向。这在 C++ 中很容易确保,它具有可定义的移动语义,但是除了“在使用之前不要设置”之外,Rust 是否提供了解决方案bar.p_foo?虽然这样做肯定会奏效,但我认为这些缓存值的移动频率不会超过(甚至接近于)它们被重用的频率,而且设置这些缓存值需​​要做一些工作指针,尤其是在嵌套/链接很深/很长的情况下。我也不想把Box子结构放在堆上。


为了澄清,这是我可以用 C++ 编写的,我想在 Rust 中复制:

struct Foo {
    int x;
    float y;
};

struct Bar {
    Foo const*pFoo;
    bool z;
};

// bear with me while I conjure up a Maybe for C++
class Cached {
public:
    // would have appropriate copy constructor/assignment

    Cached(Cached &&other) {
        m_foo = other.m_foo;
        m_bar = other.m_bar;

        if(m_foo.isJust()) {
            m_bar.pFoo = &m_foo.value();
        } // else already nullptr
    }

    // similar move assignment

private:
    Maybe<Foo> m_foo;
    Bar m_bar;
};
4

1 回答 1

1

Rust 等价物是不使用原始指针,因为原始指针用于实现我们的安全数据结构,而不是用于实现普通数据结构。

#[repr(C)]
struct Foo {
    x: i32,
    y: f32
}

#[repr(C)]
struct Bar {
    p_foo: Option<Box<Foo>>,
    z: bool
}

只要是类型而不是特征,就保证AnOption<Box<T>>与 a 完全等效(以内存中的位为单位) 。唯一的区别是它在 Rust 中使用是安全的。*const TT

这样,您甚至Cached不再需要结构,而是可以直接传递Bar对象。


我也不想把子结构装在堆上。

然后我建议你不要保留一个Bar对象,而是在需要将一个对象传递给 C 时将其变出:

#[repr(C)]
struct Foo {
    x: i32,
    y: f32
}

#[repr(C)]
struct Bar<'a> {
    p_foo: Option<&'a Foo>,
    z: bool
}

struct Cached {
    foo: Option<Foo>,
    z: bool,
}

impl Cached {
    fn bar<'a>(&'a self) -> Bar<'a> {
        Bar {
            p_foo: self.foo.as_ref(),
            z: self.z,
        }
    }
}

设置这些指针需要做一些工作,尤其是在嵌套/链接很深/很长的情况下。

这听起来很像过早的优化。不要在没有进行基准测试的地方进行优化。

于 2016-03-24T08:33:00.217 回答