< 返回版块

kaixinbaba 发表于 2020-12-15 10:41

Tags:lifetime,引用


struct InnerA {
}
struct InnerB<'a> {
    inner_a: &'a InnerA,
}
struct Outer {
    inner_a: InnerA,
    inner_b: InnerB,
}
fn main() {

    let inner_a = InnerA {};
    let inner_b = InnerB {
        inner_a: &inner_a,
    };

    let outer = Outer {
        inner_a,
        inner_b,
    };
    println!("OK");
}

我一开始的想法是这样的,outer持有两个对象,b持有a的引用,持有引用需要生命周期我能理解,但是光这样是无法编译过的,因为提示我Outerinner_b字段也需要生命周期,这个我就不太能理解了,Outer不是持有了inner_b的所有权吗,为什么还需要生命周期呢


分割线 然后我就改成了这样

struct InnerA {
}
struct InnerB<'a> {
    inner_a: &'a InnerA,
}
struct Outer<'b> {
    inner_a: InnerA,
    inner_b: InnerB<'b>,
}
fn main() {

    let inner_a = InnerA {};
    let inner_b = InnerB {
        inner_a: &inner_a,
    };

    let outer = Outer {
        inner_a,
        inner_b,
    };
    println!("OK");
}

我替Outer和字段b声明了生命周期,但是编译还是报错,这个报错我就更无法理解了

error[E0505]: cannot move out of `inner_a` because it is borrowed
  --> src/main.rs:18:9
   |
14 |         inner_a: &inner_a,
   |                  -------- borrow of `inner_a` occurs here
...
18 |         inner_a,
   |         ^^^^^^^ move out of `inner_a` occurs here
19 |         inner_b,
   |         ------- borrow later used here

我传递给b的不是a的引用么?为什么下面会报 move out of inner_a 的错误? 是我犯了什么低级错误吗,还是我理解的 Rust 根本就不对。。。 只能来论坛求助了,希望能得到大佬的回复,Rust真的好难啊。。。哭哭

评论区

写评论
作者 kaixinbaba 2020-12-15 17:08

非常感谢您的回复,我再理一理思路,心智模型还是差了好多

--
👇
Eliot00: 如果允许你移动了a,还保持b对a的引用,就相当于制造了悬垂指针,出现内存bug,所以提示cannot move out of inner_a because it is borrowed

Eliot00 2020-12-15 14:38

如果允许你移动了a,还保持b对a的引用,就相当于制造了悬垂指针,出现内存bug,所以提示cannot move out of inner_a because it is borrowed

--
👇
Eliot00: ```rust struct InnerA { } struct InnerB<'a> { inner_a: &'a InnerA, } struct Outer { inner_a: InnerA, inner_b: InnerB, } fn main() {

let inner_a = InnerA {}; // a
let inner_b = InnerB {
    inner_a: &inner_a,  // b
};

let outer = Outer {
    inner_a,  // c
    inner_b,
};
println!("OK");

}


这段代码里,在*a*处,`inner_a`持有一个了InnerA实例的所有权,*b*处,`inner_b`**借用**了`inner_a`,但是在*c*处,此时`innner_b`还*“活着”*,手上还有对a的借用,可是`inner_a`在c处,他的所有权被`outer`拿走了

lifetime标记并不能延长变量的生存期,可以理解为`inner_a`被拿去构造`Outer`的时候它的生命就到头了,可是`inner_b`还在引用它,所以就报错了

Eliot00 2020-12-15 14:35
struct InnerA {
}
struct InnerB<'a> {
    inner_a: &'a InnerA,
}
struct Outer {
    inner_a: InnerA,
    inner_b: InnerB,
}
fn main() {

    let inner_a = InnerA {}; // a
    let inner_b = InnerB {
        inner_a: &inner_a,  // b
    };

    let outer = Outer {
        inner_a,  // c
        inner_b,
    };
    println!("OK");
}

这段代码里,在a处,inner_a持有一个了InnerA实例的所有权,b处,inner_b借用inner_a,但是在c处,此时innner_b还*“活着”*,手上还有对a的借用,可是inner_a在c处,他的所有权被outer拿走了

lifetime标记并不能延长变量的生存期,可以理解为inner_a被拿去构造Outer的时候它的生命就到头了,可是inner_b还在引用它,所以就报错了

Cupnfish 2020-12-15 13:03

这里已经被借用了,然后你又需要它的所有权。。。

👇
kaixinbaba: 我知道字面意思,但是不知道怎么改,\尴尬

--
👇
langzi.me: 错误提示不是挺明白的。
inner_a: &inner_a, | -------- borrow of inner_a occurs here

作者 kaixinbaba 2020-12-15 12:01

谢谢谢谢,什么时候用Box 什么时候用 & 。。

--
👇
12101111: 你这样自引用了. 你的第一段代码是错的,是因为InnerB的类型里有生命周期参数, 完整的类型必须加上<'a>. 第二段类型改对了, 但是也是不能编译的, 因为inner_b初始化的时候借用了inner_a, 这时候inner_b的inner_a字段指向栈上的inner_a, 但是后面初始化Outer的时候, 就把inner_a move走了, 这时候inner_b存的inner_a的地址就已经无效了, 所以会报错. 所以先把inner_a放到堆上, 这样inner_a的地址就不会变了.

struct InnerA;
struct InnerB<'a> {
    inner_a: &'a InnerA,
}
struct Outer<'b> {
    inner_a: Box<InnerA>,
    inner_b: Option<InnerB<'b>>,
}
fn main() {
    let inner_a = Box::new(InnerA);
    let mut outer = Outer {
        inner_a,
        inner_b: None,
    };
    let inner_b = InnerB {
        inner_a: &outer.inner_a,
    };
    outer.inner_b = Some(inner_b);
    println!("OK");
}
作者 kaixinbaba 2020-12-15 12:00

我知道字面意思,但是不知道怎么改,\尴尬

--
👇
langzi.me: 错误提示不是挺明白的。
inner_a: &inner_a, | -------- borrow of inner_a occurs here

12101111 2020-12-15 11:27

你这样自引用了. 你的第一段代码是错的,是因为InnerB的类型里有生命周期参数, 完整的类型必须加上<'a>. 第二段类型改对了, 但是也是不能编译的, 因为inner_b初始化的时候借用了inner_a, 这时候inner_b的inner_a字段指向栈上的inner_a, 但是后面初始化Outer的时候, 就把inner_a move走了, 这时候inner_b存的inner_a的地址就已经无效了, 所以会报错. 所以先把inner_a放到堆上, 这样inner_a的地址就不会变了.

struct InnerA;
struct InnerB<'a> {
    inner_a: &'a InnerA,
}
struct Outer<'b> {
    inner_a: Box<InnerA>,
    inner_b: Option<InnerB<'b>>,
}
fn main() {
    let inner_a = Box::new(InnerA);
    let mut outer = Outer {
        inner_a,
        inner_b: None,
    };
    let inner_b = InnerB {
        inner_a: &outer.inner_a,
    };
    outer.inner_b = Some(inner_b);
    println!("OK");
}
langzi.me 2020-12-15 11:15

错误提示不是挺明白的。
inner_a: &inner_a, | -------- borrow of inner_a occurs here

1 共 8 条评论, 1 页