pointersrustmutable-reference

Change reference to inner struct, or is it possible to change the reference of a reference in rust


I'm currently trying to translate a C program to Rust.

The C programm has following structures (boiled down to MRE):

typedef struct {
    bool isActive;
    uint32_t value;
} tMyInnerStruct;

typedef struct {
    tMyInnerStruct inner1;
    tMyInnerStruct inner2;
} tMyStruct;

and in a function I would do the following pointer magic:

void myFunction()
{
    tMyStruct myStruct = initMyStuct();
    tMyInnerStruct *pActiveInner = &myStruct.inner1;
    // Do something with pActiveInner (pointing to inner1), like setting value...
    changeActiveStruct(&myStruct, &pActiveInner);
    // Do something with pActiveInner (pointing to inner2), like setting value...
}

void changeActiveStruct( tMyStruct *pMyStruct, tMyInnerStruct **ppActiveInner )
{
    if ( &pMyStruct->inner1 == *ppActiveInner ) {
        *ppActiveInner = &pMyStruct->inner2;
    } else {
        *ppActiveInner = &pMyStruct->inner1;
    }
}

No my question is: How would I achieve the same in Rust?

What I've tried so far in Rust:

#[derive(Default)]
struct MyInnerStruct {
    is_active: bool,
    value: u32,
}

#[derive(Default)]
struct MyStruct {
    inner1: MyInnerStruct,
    inner2: MyInnerStruct,
}

impl MyStruct {
    fn change_active(&mut self, active: &MyInnerStruct) -> &mut MyInnerStruct {
        if &self.inner1 as *const _ == active as *const _ {
            &mut self.inner2
        } else {
            &mut self.inner1
        }
    }
}

fn main() {
    let mut my_struct: MyStruct = Default::default();
    let mut active = &mut my_struct.inner1;
    active = my_struct.change_active(active);
    // The above complains, that my_struct cannot be borrowed mutable more than once...
}

Solution

  • You can accompilsh the same thing by using a pointer as argument instead of a reference. Because of the order that arguments and the receiver are evaluated in, you have to convert to a pointer ahead of the call and can't do it in the parameter list but this works:

    impl MyStruct {
        fn change_active(&mut self, active: *const MyInnerStruct) -> &mut MyInnerStruct {
            if &self.inner1 as *const _ == active {
                &mut self.inner2
            } else {
                &mut self.inner1
            }
        }
    }
    
    fn main() {
        let mut my_struct: MyStruct = Default::default();
        let mut active = &mut my_struct.inner1; 
    
        active = { 
            let active = active as *const _;
            my_struct.change_active(active)
        };
    }
    

    Playground


    Minor detail, change_active does no longer seem like an apropriate name since the Rust version doesn't actually change active maybe new_active or something else still is a better name for the method