The same object with different API faces at compile time?

I have an object that can be in either of two modes: source or destination. It is always in one of them, and is always known at compile time (when passed in an object that you know if you are going to read or write it).

I can put all the methods on the same object and just assume that I won't be called incorrectly or mistakenly when I do this, or I thought I could make two tuples of one single base object and instead attach methods to those tuples structures. The methods do not overlap almost completely.

It's kind of an abuse of the fact that both tuple structures have the same layout and there is zero overhead for butts and tuples.

Think about it, similar to Java ByteBuffer

and its related classes where you write, then flip, then read, then sit back and write more. Moreover, it will break usage errors.

However, this seems a little out of the ordinary and might be too confusing for such a small problem. And it seems like there is a better way to do it - only requirement - there is zero overhead, hence dynamic dispatch.

struct Underlying {
    a: u32,
    b: u32,

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)} }

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();

    let b = a.tob();

    let aa = b.toa();



source to share

1 answer

First of all, you don't seem to understand how property works in Rust; you can read the Ownership chapter in the Rust Book. In particular, the way to re-anti-aliasing the original FaceA

is how you specifically include what you say you want to avoid. Also, all borrowings are immutable, so it is not clear how you are going to perform any mutation.

So I wrote a new example from scratch that involves switching between two types with non-overlapping interfaces ( viewing in the arena ).

pub struct Inner {
    pub value: i32,

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

pub struct Upper(Inner);

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

    pub fn into_downer(self) -> Downer {

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

pub struct Downer(Inner);

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

    pub fn into_upper(self) -> Upper {

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

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

    let mut b = a.into_downer();

    let mut c = b.into_upper();


fn show_i32(v: i32) {
    println!("v: {:?}", v);


Here the methods are into_upper

and into_downer

consume the value of the object, preventing anyone from using it after (try accessing a

after the call a.into_downer()


It doesn't have to be particularly ineffective; there is no heap here, and Rust is pretty good at moving values โ€‹โ€‹efficiently. In case you're wondering, this is what the function main

compiles with optimizations enabled:

mov edi, -1
jmp _ZN8show_i3220h2a10d619fa41d919UdaE


It literally builds the entire program (except for the function show

I specifically told it not to inline). If profiling doesn't show that this is a serious performance issue, then I wouldn't bother with it.



All Articles