< 返回版块

kipade 发表于 2025-08-04 13:23

Tags:lifetime

基本需求就是这样,A结构体包含B结构体,B结构体也要包含A结构体的引用,以访问其它成员,示例代码大致如下,怎么也改不对,DeepSeek也没能帮我改对,报错和生命周期相关,求助:

   use std::rc::{Rc,Weak};
   use std::cell::RefCell;

   trait TestTrait<'a> {
       fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool;
       fn get_string(&self) -> String;
   }

   struct Test1<'a> {
       str: String,
       test2: Test2<'a>
   }

   struct Test2<'a> {
       parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>
   }

   impl<'a> Default for Test2<'a> {
       fn default() -> Self {
           Test2{parent:None}
       }
   }

   impl<'a> TestTrait<'a> for Test2<'a> {
       fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
           self.parent = parent; //在这个地方怎么也过不去
           false
       }
       fn get_string(&self) -> String {
           if let Some(ref t2) = self.parent {
               if let Some(rc) = t2.upgrade() {
                   let b = rc.borrow();
                   return b.get_string();
               }
           }
           "".to_string()
       }
   }

   impl<'a> Default for Test1<'a> {
       fn default() -> Self {
           Test1{
               str:"Test1".to_string(), 
               test2: Test2::default()
           }
       }
   }

   impl<'a> TestTrait<'a> for Test1<'a> {
       fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
           let rc_self = Rc::new(RefCell::new(self as &dyn TestTrait<'a>));
           let weak_self = Rc::downgrade(&rc_self);
           self.test2.test_func(Some(weak_self));
           false
       }
       
       fn get_string(&self) -> String {
           self.test2.get_string();
           self.str.clone()
       }
   }

   fn main() {
       let mut test1 = Test1::default();
       test1.test_func(None);
       println!("{}", test1.get_string());
   }
   

评论区

写评论
mi 2025-08-06 17:51

使用clone()呢?

xiaoyaou 2025-08-06 14:58

我单从技术(trick)的角度帮你修复一下编译器不通过的问题🤭 -- 直接截断编译器对week_self的引用溯源:

use std::cell::RefCell;
use std::marker::PhantomPinned;
use std::rc::{Rc, Weak};

trait TestTrait<'a> {
    fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool;
    fn get_string(&self) -> String;
}

struct Test1<'a> {
    str: String,
    test2: Test2<'a>,
    // 注意:因为test2可能引用到Test1,而Test1移动会导致test2原引用失效,所以
    // PhantomPinned 用于标记 Test1 不应该被移动
    // 此标记只表达不移动的意图,实际还需要借助Pin<...>完成真正的不可移动限制
    _pin: PhantomPinned,
}

struct Test2<'a> {
    parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>,
}

impl<'a> Default for Test2<'a> {
    fn default() -> Self {
        Test2 { parent: None }
    }
}

impl<'a> TestTrait<'a> for Test2<'a> {
    fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
        // 标记执行
        println!("Test2 test_func called");
        self.parent = parent;
        false
    }
    fn get_string(&self) -> String {
        if let Some(ref t2) = self.parent {
            if let Some(rc) = t2.upgrade() {
                let b = rc.borrow();
                return b.get_string();
            }
        }
        // 标记输出
        "Parent Not Found".to_string()
    }
}

impl<'a> Default for Test1<'a> {
    fn default() -> Self {
        Test1 {
            str: "Test1".to_string(),
            test2: Test2::default(),
            _pin: PhantomPinned,
        }
    }
}

impl<'a> TestTrait<'a> for Test1<'a> {
    fn test_func(&mut self, _parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
        // 丢弃引用来源追溯,重新借用为新引用(任意生命周期)
        // 利用自引用结构的父子包含性,来确保子结构中向上引用的正确性
        // 在封装良好的情况下是安全的
        let reborrow = unsafe { &*(self as &dyn TestTrait<'a> as *const dyn TestTrait<'a>) };
        let rc_self = Rc::new(RefCell::new(reborrow));
        let weak_self = Rc::downgrade(&rc_self);
        self.test2.test_func(Some(weak_self));
        false
    }

    fn get_string(&self) -> String {
        // 注意:
        // 因为是在子函数里设置的parent,Rc随即就会失效
        // 所以这里不会死循环,但是正常情况下parent有效则会出现父子死循环调用,没有意义
        println!("Child: {}", self.test2.get_string());
        self.str.clone()
    }
}

fn main() {
    let mut test1 = Test1::default();
    test1.test_func(None);
    println!("{}", test1.get_string());
    test(test1);
}
xiaoyaou 2025-08-06 14:26

这 这不对吧(鸡汤状)

问题好像也不在你说的地方:

    fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
        self.parent = parent; //在这个地方怎么也过不去
        false
    }

上面这段逻辑上是没有问题的,编译器不通过的地方在这里:

    fn test_func(&mut self, parent: Option<Weak<RefCell<&'a dyn TestTrait<'a>>>>) -> bool {
           let rc_self = Rc::new(RefCell::new(self as &dyn TestTrait<'a>));
           let weak_self = Rc::downgrade(&rc_self);
           self.test2.test_func(Some(weak_self)); // <-- <-- 不可变引用和可变引用重叠了
           false
    }

这地方有两个问题:一是test_func需要&mut self,但是week_self已经拥有&self了;第二个就是,&mut self没有显式标注生命周期就是临时生命周期,所以week_self也会限制在该临时生命周期,无法满足test_func'a的要求

作者 kipade 2025-08-05 10:46

谢谢,你这个高级啊,只是,我的水平还用不了这个

--
👇
Bai-Jinlin: 自引用需要用Pin来实现,我这里用了pinned_init库进行原地构造,你可以参考下我的实现。

use pinned_init::{InPlaceInit, pin_data, pin_init};
use std::{marker::PhantomPinned, pin::Pin, ptr::NonNull};

#[pin_data]
struct Test1 {
    name: String,
    test2: Test2,
    #[pin]
    _pin: PhantomPinned,
}
struct Test2 {
    parent: NonNull<Test1>,
}

impl Test1 {
    fn new(name: String) -> Pin<Box<Self>> {
        let init = pin_init!(&this in Self{
            name,
            test2 :Test2{
                parent:this
            },
            _pin:PhantomPinned

        });
        Box::pin_init(init).unwrap()
    }
    fn get_test2(self: Pin<&Self>) -> Pin<&Test2> {
        unsafe { self.map_unchecked(|s| &s.test2) }
    }
}
impl Test2 {
    fn get_parent(self: Pin<&Self>) -> Pin<&Test1> {
        unsafe { self.map_unchecked(|s| s.parent.as_ref()) }
    }
}

fn main() {
    let test1 = Test1::new("test1".to_string());
    assert_eq!(test1.as_ref().get_test2().get_parent().name, "test1");
}

作者 kipade 2025-08-05 10:44

解析的时候地确是可以,但现在的问题是,我要把它解成数据结构,然后,xml文件就不再需要了

--
👇
asuper: 栈溢出是因为两个结构反复调用get_string(),类似于递归没有写退出条件,可以限制一下调用深度。

从你的需求来讲,也可以通过操作path,再去xml中索引的方式来取父节点。

asuper 2025-08-05 09:49

栈溢出是因为两个结构反复调用get_string(),类似于递归没有写退出条件,可以限制一下调用深度。

从你的需求来讲,也可以通过操作path,再去xml中索引的方式来取父节点。

作者 kipade 2025-08-04 17:33

谢谢,的确是溢出了。我的本来需求跟这个类似,这是我的一个简化验证版本。我的事实上应用是,解析一个xml描述的数据结构,每一个层级对应一个数据结构,然后,因为一些孩子结构可能需要用到父结构甚至是祖先结构里面的内容,所以,我需要子结构中拿到父结构,所以就有了这么一个需求。

--
👇
ThinkCodeStudio: 我不知道你这段代码的真实目的, 我修改后的代码可以运行了, 但是程序出现了栈溢出, 在我注释的地方程序陷入了死循环.

use std::cell::RefCell;
use std::rc::{Rc, Weak};

trait TestTrait {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool;
    fn get_string(&self) -> String;
}

struct Test1 {
    str: String,
    test2: Test2,
}

struct Test2{
    parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>,
}

impl Default for Test2 {
    fn default() -> Self {
        Test2 { parent: None }
    }
}

impl TestTrait for Test2 {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool {
        self.parent = parent;
        false
    }
    fn get_string(&self) -> String {
        // thread 'main' has overflowed its stack
        // if let Some(ref t2) = self.parent {
        //     if let Some(rc) = t2.upgrade() {
        //         rc.borrow().get_string()
        //     } else {
        //         "".to_string()
        //     }
        // }
        // else{
        //     "".to_string()
        // }
        return "Test2".to_string();
    }
}

impl Default for Test1 {
    fn default() -> Self {
        Test1 {
            str: "Test1".to_string(),
            test2: Test2::default(),
        }
    }
}

impl TestTrait for Test1 {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool {
        self.test2.test_func(parent);
        false
    }

    fn get_string(&self) -> String {
        self.test2.get_string();
        self.str.clone()
    }
}

fn main() {
    let test1: Rc<RefCell<Box<dyn TestTrait>>> = Rc::new(RefCell::new(Box::new(Test1::default())));
    let test1_weak = Rc::downgrade(&test1);
    test1.borrow_mut().test_func(Some(test1_weak));
    println!("{}", test1.borrow().get_string());
}

Bai-Jinlin 2025-08-04 15:34

自引用需要用Pin来实现,我这里用了pinned_init库进行原地构造,你可以参考下我的实现。

use pinned_init::{InPlaceInit, pin_data, pin_init};
use std::{marker::PhantomPinned, pin::Pin, ptr::NonNull};

#[pin_data]
struct Test1 {
    name: String,
    test2: Test2,
    #[pin]
    _pin: PhantomPinned,
}
struct Test2 {
    parent: NonNull<Test1>,
}

impl Test1 {
    fn new(name: String) -> Pin<Box<Self>> {
        let init = pin_init!(&this in Self{
            name,
            test2 :Test2{
                parent:this
            },
            _pin:PhantomPinned

        });
        Box::pin_init(init).unwrap()
    }
    fn get_test2(self: Pin<&Self>) -> Pin<&Test2> {
        unsafe { self.map_unchecked(|s| &s.test2) }
    }
}
impl Test2 {
    fn get_parent(self: Pin<&Self>) -> Pin<&Test1> {
        unsafe { self.map_unchecked(|s| s.parent.as_ref()) }
    }
}

fn main() {
    let test1 = Test1::new("test1".to_string());
    assert_eq!(test1.as_ref().get_test2().get_parent().name, "test1");
}

ThinkCodeStudio 2025-08-04 15:07

我不知道你这段代码的真实目的, 我修改后的代码可以运行了, 但是程序出现了栈溢出, 在我注释的地方程序陷入了死循环.

use std::cell::RefCell;
use std::rc::{Rc, Weak};

trait TestTrait {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool;
    fn get_string(&self) -> String;
}

struct Test1 {
    str: String,
    test2: Test2,
}

struct Test2{
    parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>,
}

impl Default for Test2 {
    fn default() -> Self {
        Test2 { parent: None }
    }
}

impl TestTrait for Test2 {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool {
        self.parent = parent;
        false
    }
    fn get_string(&self) -> String {
        // thread 'main' has overflowed its stack
        // if let Some(ref t2) = self.parent {
        //     if let Some(rc) = t2.upgrade() {
        //         rc.borrow().get_string()
        //     } else {
        //         "".to_string()
        //     }
        // }
        // else{
        //     "".to_string()
        // }
        return "Test2".to_string();
    }
}

impl Default for Test1 {
    fn default() -> Self {
        Test1 {
            str: "Test1".to_string(),
            test2: Test2::default(),
        }
    }
}

impl TestTrait for Test1 {
    fn test_func(&mut self, parent: Option<Weak<RefCell<Box<dyn TestTrait>>>>) -> bool {
        self.test2.test_func(parent);
        false
    }

    fn get_string(&self) -> String {
        self.test2.get_string();
        self.str.clone()
    }
}

fn main() {
    let test1: Rc<RefCell<Box<dyn TestTrait>>> = Rc::new(RefCell::new(Box::new(Test1::default())));
    let test1_weak = Rc::downgrade(&test1);
    test1.borrow_mut().test_func(Some(test1_weak));
    println!("{}", test1.borrow().get_string());
}

1 共 9 条评论, 1 页