在关联函数中指定对成员变量返回参数的引用的生命周期。

3
我想要一个名为CatMaker的结构体,可以创建一个Cat,但会在内部存储对它的引用,以便稍后调用该Cat(可能有一个CatMaker.get_child_color(&self)函数)。我相信这意味着CatMaker不能比Cat存在更久(否则它将尝试进行解除引用,而Cat将不再存在),编译器似乎也同意。
以下是我想要完成的代码示例:
pub enum CoatColor {
    Black,
    Tabby,
}

pub struct Cat {
    color: CoatColor,
}

pub struct CatMaker<'a> {
    child: Option<&'a Cat>,
}

impl<'a> CatMaker<'a> {
    pub fn new() -> CatMaker<'a> {
        CatMaker{ child: None }
    }

    pub fn make_cat(&mut self, color: CoatColor) -> Cat {
        let new_cat = Cat{ color: color };
        self.child = Some(&new_cat); // commenting out this line will allow it to compile
        new_cat
    }
}

fn main() {
    let mut my_cat_maker = CatMaker::new();
    let mut my_cat = my_cat_maker.make_cat(CoatColor::Black);
    my_cat.color = CoatColor::Tabby;
}

我在尝试确定如何指定CatMaker.make_catself.child的生命周期,但遇到了困难。


为什么我不能在同一个结构体中存储值和对该值的引用相同的根本问题。当您移动一个值(例如返回您的Cat)时,对它的引用将不再有效。 - Shepmaster
谢谢您指出这一点。看起来答案可能类似于使用owning_ref的方式。 - ampron
1个回答

2

您可以让CatMaker拥有Cat,并使make_cat返回对Cat的引用,而不是返回Cat本身。

pub enum CoatColor {
    Black,
    Tabby,
}

pub struct Cat {
    color: CoatColor,
}

pub struct CatMaker {
    child: Option<Cat>,
}

impl CatMaker {
    pub fn new() -> CatMaker {
        CatMaker { child: None }
    }

    pub fn make_cat(&mut self, color: CoatColor) -> &mut Cat {
        let new_cat = Cat { color: color };
        self.child = Some(new_cat);
        self.child.as_mut().unwrap()
    }
}

fn main() {
    let mut my_cat_maker = CatMaker::new();
    let mut my_cat = my_cat_maker.make_cat(CoatColor::Black);
    my_cat.color = CoatColor::Tabby;
}

然而,这有一个很大的限制:只要保留了make_cat的结果(在这里,它被存储在my_cat中),就不能使用my_cat_maker。因为my_catmy_cat_maker进行了可变借用,而Rust不允许两个可变借用同时在同一对象上使用。
如果您无法接受此限制,则需要使用另一个工具来管理Cat的生命周期。这样的工具是Rc,它是一个引用计数的对象引用。如果您还需要能够改变Cat,则需要将RcRefCell结合使用,以允许在Rc中修改对象。
use std::cell::RefCell;
use std::rc::Rc;

pub enum CoatColor {
    Black,
    Tabby,
}

pub struct Cat {
    color: CoatColor,
}

pub struct CatMaker {
    child: Option<Rc<RefCell<Cat>>>,
}

impl CatMaker {
    pub fn new() -> CatMaker {
        CatMaker { child: None }
    }

    pub fn make_cat(&mut self, color: CoatColor) -> Rc<RefCell<Cat>> {
        let new_cat = Rc::new(RefCell::new(Cat { color: color }));
        self.child = Some(new_cat.clone());
        new_cat
    }
}

fn main() {
    let mut my_cat_maker = CatMaker::new();
    let my_cat = my_cat_maker.make_cat(CoatColor::Black);
    my_cat.borrow_mut().color = CoatColor::Tabby;
}

make_cat函数中,.clone()调用会克隆Rc对象,这将创建一个指向同一对象的新引用,并增加引用计数。当所有相关的Rc对象都被丢弃时,引用计数的对象也会被丢弃。
main函数中,我们需要调用borrow_mut()来访问Catborrow_mut()返回一个RefMut对象,该对象保护Cat不被进一步借用,直到RefMut被丢弃。如果在可变借用处于活动状态时尝试再次借用Cat,则程序将出现错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接