The lifetime, how to make this example run?


#1
struct A {
    name:String,
}

impl A {
    fn go(&self) {}
}

struct B<'a> {
    name:String,
    a:&'a A,
}

impl<'a> B<'a> {

    fn go(&self) {
        //do something different by self.name
        self.a.go();
    }

}

struct C<'a> {
    a:A,
    b1:B<'a>,
    b2:B<'a>,
}

impl<'a> C<'a> {

    fn b_go(&self, index:i32) {
        if index == 1 {
            self.b1.go();
        }
        else
        {
            self.b2.go();
        }
    }

    fn a_go(&self) {
        self.a.go();
    }

}

fn factory<'a>() -> C<'a> {
    let num = 5;
    let a = A {
        name:"a".to_string(),
    };
    let b1 = B {
        name:"b1".to_string(),
        a:&a,
    };
    let b2 = B {
        name:"b2".to_string(),
        a:&a,
    };
    let c = C {
        a:a,
        b1:b1,
        b2:b2,
    };
    c
}

fn main() {
    let c = factory();
}

What should I do?


#2

Runnable, on the playpen: http://is.gd/6XM6JI

It’s always good to also check out the error message, it has helpful hints. In our case:

<anon>:54:12: 54:13 error: `a` does not live long enough
<anon>:54         a:&a,
                     ^
<anon>:47:27: 66:2 note: reference must be valid for the lifetime 'a as defined on the block at 47:26...
<anon>:47 fn factory<'a>() -> C<'a> {
<anon>:48     let num = 5;
<anon>:49     let a = A {
<anon>:50         name:"a".to_string(),
<anon>:51     };
<anon>:52     let b1 = B {
          ...
<anon>:51:7: 66:2 note: ...but borrowed value is only valid for the block suffix following statement 1 at 51:6
<anon>:51     };
<anon>:52     let b1 = B {
<anon>:53         name:"b1".to_string(),
<anon>:54         a:&a,
<anon>:55     };
<anon>:56     let b2 = B {
          ...

The issue is, a does not live long enough, because it must live as long as the <'a> in your function signature, but only lives until the end of the function. In other words, Rust has detected a dangling pointer for you.

However, you want c to own a, while also owning b1 and b2 to have references to it. This kind of structure is hard for Rust: If we move a, then b1 and b2's references would need to be updated, but there’s no way to do that. And this is what happens: you create a, and then take two references to it, and then move a into c.

It’s just a bit too early for me to hack out a solution here, hopefully someone else can give you more guidance on that, but that’s your core problem. I’m not sure if you can make it work without using shared ownership.


#3

Thank you.
I find a way. Using the Arc, but I don’t know if it is right.

use std::sync::{Arc};

struct A {
    name:String,
}

impl A {
    fn go(&self) {
        println!("from the {}.", self.name);
    }
}

struct B {
    name:String,
    a:Arc<A>,
}

impl B {

    fn go(&self) {
        println!("from {}.", self.name);
        //do something different by self.name
        self.a.go();
    }

}

struct C {
    a:Arc<A>,
    b1:B,
    b2:B,
}

impl C {

    fn b_go(&self, index:i32) {
        if index == 1 {
            self.b1.go();
        }
        else
        {
            self.b2.go();
        }
    }

    fn a_go(&self) {
        self.a.go();
    }

}

fn factory() -> C {
    let num = 5;
    let a = A {
        name:"a".to_string(),
    };
    let a_data = Arc::new(a);
    let b1 = B {
        name:"b1".to_string(),
        a:a_data.clone(),
    };
    let b2 = B {
        name:"b2".to_string(),
        a:a_data.clone(),
    };
    let c = C {
        a:a_data,
        b1:b1,
        b2:b2,
    };
    c
}

fn main() {
    let c = factory();
    c.b_go(1);
    c.b_go(2);
    c.a_go();
}

#4

You can’t move a struct while it’s borrowed (the references would be invalidated). In your case, you’re moving a into c while it’s borrowed by b1 and b2.

One solution is to use an Rc to share a between b1, b2, and c equally. However, you would be better off finding a ownership hierarchy where every data structure has only one owner.


#5

You don’t need an Arc because you will only ever reference an A from one thread at a time. Rc will give you better performance.


#6

Thanks.

I have a problem with thread safe.

use std::thread;
use std::sync::{Arc, Mutex};

struct A {
    name:String,
}

struct B {
    a:A
}

impl A {

    fn go(&self)
    {
        println!("a go....");
    }

}

fn main() {
    let a = A {
        name:"a".to_string(),
    };
    let arc_b = Arc::new(B {a:a});
    for i in 0..3 {
        //increases the internal count
        let arc_b = arc_b.clone();
        thread::spawn(move || {
            arc_b.a.go();
        });
    }
    thread::sleep_ms(500);
}

Is that means i passed arc_b.a that isn’t implement sync and send to another thread?
And if i mutex the shared data of arc_b.a, is that thread safe?


#7

It’s thread safe as is. You only need a mutex if you want to modify an object from multiple threads.