我有一个可以处于两种模式之一的对象:源或接收器。它总是在其中之一中,并且在编译时总是已知的(当传递对象时,您清楚地知道您是否要读取或写入它)。
我可以将所有方法放在同一个对象上,假设我这样做时不会被不正确地调用或出错,或者我想我可以做两个 单个底层对象的元组结构,并将方法附加到这些元组结构。这些方法几乎完全不相交。
这有点滥用了两个元组结构具有相同布局并且转换和元组存储的开销为零这一事实。
把它想成类似于 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_upper
和 into_downer
方法消耗主题值,防止任何人之后使用它(尝试访问a
在调用 a.into_downer()
之后。
这应该不是特别低效;这里没有堆分配,Rust 非常擅长有效地移动值。如果你很好奇,这就是 main
函数在启用优化后编译的结果:
mov edi, -1
jmp _ZN8show_i3220h2a10d619fa41d919UdaE
它确实内联了整个程序(除了 show
函数,我特别告诉它不要内联)。除非分析表明这是一个严重的性能问题,否则我不会担心。
关于rust - 编译时具有不同 API 接口(interface)的同一对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31592916/