A problem about refer in struct

I test the code as following

#[derive(Debug)]
struct Test1 <'a> {
    a: [u8; 8],
    b: Option<&'a [u8]>,
}
impl <'a> Test1 <'a> {
    fn new()-> Self {
        let a = ['1' as u8; 8];
        Self {
            a,
            b: None,
        }
    }
    fn init(&'a mut self) {
        self.b = Some(&self.a);
    }
}

fn main() {
    let mut a = Test1::new();
    a.init();
    
   //error: cannot borrow `a` as immutable because it is also borrowed as mutable
    let b = &a;  
}

These codes are compiled error because a has been mut borrowed when calling a.init(), and it immute borrowed at statement let b=&a.

I changed the code as following

fn main() {
    let mut a = Test1::new();
    a.b = Some(&a.a);    // change this row from a.init()
 
   //It is OK
    let b = &a;  
}

But this is not a good method because the inner fields of struct are exposed. Then how can I use the fn of struct to implement the same function?
Pls help, thanks.

Please don't do this. You are trying to create a self referential type, and that is notoriously difficult to do correctly in Rust for good reason. Consider refactoring your code to store the array outside of Test1. By doing th you are moving ownership of the array to a level where it could be readily handled. Then just pass a reference to the array to Test1

Self referential types don't play well with Rust's trivial destructive moves. (Moves boil down to just a memcpy). So you can't fixup the type after every move. This means self referential types are either unusable, or require very tricky unsafe code.

Edit: another option is to store a std::ops: Range instead of a slice, and just generate the subslice of the fly when you need it using the range. Ranges can be constructed like this: 0..array.len()

4 Likes

Thanks for your kindly reply and suggestion.
Rust is very good to force us to program more soundly codes.

I test this case just want to know more detail about lifettime of Rust. I wonder whether if there exist a better way to implement this case. Can you give me more about this?

The core idea of lifetime parameters is that they specify lifetimes outside you type. But what lifetime outside of Test1 would satisfy 'a. There is none, because 'a must be the lifetime of Test1.

In this case I think ranges would be the best solution. If you really wanted to use lifetimes, then this is how I would do it.

#[derive(Debug)]
struct Test {
    a: [u8; 8],
}

#[derive(Debug)]
struct Test1View<'a> {
    parent: &'a Test1,
    b: &'a [u8],
}

impl Test1 {
    fn new()-> Self {
        Self {
            a: [b'1'; 8],
        }
    }
    fn view(&self) -> Test1View<'_> {
        Test1View { parent: self, b: &self.a }
    }
}

Thanks for patient reply.
You said using ranges would be the best way. How to use ranges in this case, can you give me more detail? Thanks again.

I think it would look something like this:

struct Test1 {
    a: [u8; 8],
    b: std::ops::Range<usize>,
}

impl Test1 {
    fn new() -> Self {
        let a = [1u8; 8];
        Self { a, b: 0..a.len() }
    }

    fn subslice(&self) -> &[u8] {
        &self.a[self.b.clone()]
    }
}

fn main() {
    let a = Test1::new();
    let b = &a;

    println!("{:?}", b.subslice());
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.