编译时具有不同API面的相同对象?

时间:2015-07-23 16:20:04

标签: rust

我有一个可以处于两种模式中的对象:源或接收器。它始终位于其中一个中,并且在编译时始终是已知的(当您传递给您知道的对象时,如果您要读取或写入它显然)。

我可以将所有方法放在同一个对象上,只是假设我不会被调用不正确或出错,或者我认为我可以做两个 单个底层对象的元组结构,并将方法附加到那些元组结构。这些方法几乎完全不相交。

有点滥用这样一个事实,即两个元组结构具有相同的布局,并且对于转换和元组存储没有任何开销。

认为这类似于Java ByteBuffer和您编写的相关类,然后翻转然后读取然后翻转并写入更多。除此之外会发现使用中的错误。

然而,这似乎有点不寻常,可能会因为这么小的问题而过于混乱。似乎有更好的方法可以做到这一点 - 只需要零开销,因此没有动态调度。

https://play.rust-lang.org/?gist=280d2ec2548e4f38e305&version=stable

#[derive(Debug)]
struct Underlying {
    a: u32,
    b: u32,
}

#[derive(Debug)]
struct FaceA(Underlying);

impl FaceA {
    fn make() -> FaceA { FaceA(Underlying{a:1,b:2}) }
    fn doa(&self) { println!("FaceA do A {:?}", *self); }
    fn dou(&self) { println!("FaceA do U {:?}", *self); }
    fn tob(&self) -> &FaceB {  unsafe{std::mem::transmute::<&FaceA,&FaceB>(self)} }
}

#[derive(Debug)]
struct FaceB(Underlying);

impl FaceB {
    fn dob(&self) { println!("FaceB do B {:?}", *self); }
    fn dou(&self) { println!("FaceB do U {:?}", *self); }
    fn toa(&self) -> &FaceA {  unsafe{std::mem::transmute::<&FaceB,&FaceA>(self)} }
}

fn main() {
    let a = FaceA::make();
    a.doa();
    a.dou();

    let b = a.tob();
    b.dob();
    b.dou();

    let aa = b.toa();
    aa.doa();
    aa.dou();
}

1 个答案:

答案 0 :(得分:4)

首先,您似乎并不了解Rust的所有权如何运作;您可能想阅读Ownership chapter of the Rust Book。具体来说,您对原始FaceA进行重新别名化的方式就是如何专门启用您想要避免的内容。此外,所有借款都是不可改变的,因此不清楚你打算如何做任何突变。

因此,我从头开始编写了一个新示例,涉及两种具有不相交接口的类型(view on playpen)。

#[derive(Debug)]
pub struct Inner {
    pub value: i32,
}

impl Inner {
    pub fn new(value: i32) -> Self {
        Inner {
            value: value,
        }
    }
}

#[derive(Debug)]
pub struct Upper(Inner);

impl Upper {
    pub fn new(inner: Inner) -> Self {
        Upper(inner)
    }

    pub fn into_downer(self) -> Downer {
        Downer::new(self.0)
    }

    pub fn up(&mut self) {
        self.0.value += 1;
    }
}

#[derive(Debug)]
pub struct Downer(Inner);

impl Downer {
    pub fn new(inner: Inner) -> Self {
        Downer(inner)
    }

    pub fn into_upper(self) -> Upper {
        Upper::new(self.0)
    }

    pub fn down(&mut self) {
        self.0.value -= 1;
    }
}

fn main() {
    let mut a = Upper::new(Inner::new(0));
    a.up();

    let mut b = a.into_downer();
    b.down();
    b.down();
    b.down();

    let mut c = b.into_upper();
    c.up();

    show_i32(c.0.value);
}

#[inline(never)]
fn show_i32(v: i32) {
    println!("v: {:?}", v);
}

此处,into_upperinto_downer方法使用主题值,阻止任何人在之后使用它(尝试在调用{{a之后访问a.into_downer() 1}})。

这不应该特别低效;这里没有堆分配,Rust非常擅长有效地移动值。如果您有点好奇,这就是main函数在启用优化的情况下编译的内容:

mov edi, -1
jmp _ZN8show_i3220h2a10d619fa41d919UdaE

它实际上是整个程序的内容(除了我明确告诉它的show函数而不是内联)。除非分析显示这是一个严重的性能问题,否则我不会担心它。