rust - 编译时具有不同 API 接口(interface)的同一对象?

标签 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();
}

最佳答案

首先,您似乎不了解所有权在 Rust 中的工作原理;您可能想阅读 Ownership chapter of the Rust Book .具体来说,您不断为原始 FaceA 重新设置别名的方式就是您如何专门启用您所说的想要避免的事情。此外,所有借用都是不可变的,因此不清楚您打算如何进行任何类型的变更。

因此,我从头开始编写了一个新示例,涉及在具有不相交接口(interface) (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() 之后。

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

mov edi, -1
jmp _ZN8show_i3220h2a10d619fa41d919UdaE

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

关于rust - 编译时具有不同 API 接口(interface)的同一对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31592916/

相关文章:

rust - "Unable to update registry: The server name or address could not be resolved"为依赖

testing - 运行 Rust cargo test 并让测试运行器将任何日志输出显示到控制台 stdout 或 stderr 的方法有哪些?

concurrency - Rust vs Go 并发网络服务器,为什么 Rust 在这里很慢?

unicode - UTF-8 编码错误的索引表示什么?

opengl - Piston 中的 InvalidEnum 错误

rust - 如何初始化数组并保存对其项的引用?

rust - 无法在使用rust 柴油中使用通用参数实现特征

Rust 结构中的生命周期范围

string - 为 HashMap<_, String> 返回默认 &str

Rust 比较 Option<Vec<u8>> 和 Option<&[u8]>