【问题标题】:Building clean and flexible binary trees in Rust在 Rust 中构建干净灵活的二叉树
【发布时间】:2022-11-18 06:04:16
【问题描述】:

我正在使用二叉树来创建一个简单的计算图。我知道链表在 Rust 中是一个难题,但对于我正在做的事情来说,它是一种非常方便的数据结构。我尝试对子节点使用 BoxRc<RefCell>,但没有达到我想要的效果,所以我使用了 unsafe

use std::ops::{Add, Mul};

#[derive(Debug, Copy, Clone)]
struct MyStruct {
    value: i32,
    lchild: Option<*mut MyStruct>,
    rchild: Option<*mut MyStruct>,
}

impl MyStruct {
    unsafe fn print_tree(&mut self, set_to_zero: bool) {
        if set_to_zero {
            self.value = 0;
        }
        println!("{:?}", self);
    
        let mut nodes = vec![self.lchild, self.rchild];
        while nodes.len() > 0 {
            let child;
            match nodes.pop() {
                Some(popped_child) => child = popped_child.unwrap(),
                None => continue,
            }

            if set_to_zero {
                (*child).value = 0;
            }
            println!("{:?}", *child);
            
            if !(*child).lchild.is_none() {
                nodes.push((*child).lchild);
            }
            if !(*child).rchild.is_none() {
                nodes.push((*child).rchild);
            }
        }
        
        println!("");
    }
}

impl Add for MyStruct {
    type Output = Self;
    fn add(self, other: Self) -> MyStruct {
        MyStruct{
            value: self.value + other.value,
            lchild: Some(&self as *const _ as *mut _),
            rchild: Some(&other as *const _ as *mut _),
        }
    }
}

impl Mul for MyStruct {
   type Output = Self;
   fn mul(self, other: Self) -> Self {
        MyStruct{
            value: self.value * other.value,
            lchild: Some(&self as *const _ as *mut _),
            rchild: Some(&other as *const _ as *mut _),
        }
   }
}

fn main() {
    let mut tree: MyStruct;
    
    {
        let a = MyStruct{ value: 10, lchild: None, rchild: None };
        let b = MyStruct{ value: 20, lchild: None, rchild: None };
        
        let c = a + b;
        println!("c.value: {}", c.value); // 30
        
        let mut d = a + b;
        println!("d.value: {}", d.value); // 30
        
        d.value = 40;
        println!("d.value: {}", d.value); // 40
        
        let mut e = c * d;
        println!("e.value: {}", e.value); // 1200
        
        unsafe {
            e.print_tree(false); // correct values
            e.print_tree(true); // all zeros
            e.print_tree(false); // all zeros, everything is set correctly
        }
        
        tree = e;
    }
    
    unsafe { tree.print_tree(false); } // same here, only zeros
}

Link to the playground

老实说,我不介意使用unsafe那么多,但是有安全的方法吗?在这里使用unsafe有多糟糕?

【问题讨论】:

  • 你所拥有的东西在我看来很不可靠。你应该可以为两个孩子做Option&lt;Box&lt;MyStruct&gt;&gt;
  • 这是非常不正确的。您将删除除一个节点以外的每个节点,同时保留和取消引用指向它们的指针。

标签: data-structures rust graph binary-tree


【解决方案1】:

你可以只装箱两个孩子,因为你有一个单向树:

use std::ops::{Add, Mul};
use std::fmt;

#[derive(Clone)]
struct MyStruct {
    value: i32,
    lchild: Option<Box<MyStruct>>,
    rchild: Option<Box<MyStruct>>,
}

impl fmt::Debug for MyStruct {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.debug_struct("MyStruct")
            .field("value", &self.value)
            .field("lchild", &self.lchild.as_deref())
            .field("rchild", &self.rchild.as_deref())
            .finish()
    }
}

impl MyStruct {
    fn print_tree(&mut self, set_to_zero: bool) {
        if set_to_zero {
            self.value = 0;
        }

        println!("MyStruct {{ value: {:?}, lchild: {:?}, rchild: {:?} }}", self.value, &self.lchild as *const _, &self.rchild as *const _);

        if let Some(child) = &mut self.lchild {
            child.print_tree(set_to_zero);
        }

        if let Some(child) = &mut self.rchild {
            child.print_tree(set_to_zero);
        }
    }
}

impl Add for MyStruct {
    type Output = Self;
    fn add(self, other: Self) -> MyStruct {
        MyStruct {
            value: self.value + other.value,
            lchild: Some(Box::new(self)),
            rchild: Some(Box::new(other)),
        }
    }
}

impl Mul for MyStruct {
    type Output = Self;
    fn mul(self, other: Self) -> Self {
        MyStruct {
            value: self.value * other.value,
            lchild: Some(Box::new(self)),
            rchild: Some(Box::new(other)),
        }
    }
}

fn main() {
    let tree = {
        let a = MyStruct {
            value: 10,
            lchild: None,
            rchild: None,
        };
        let b = MyStruct {
            value: 20,
            lchild: None,
            rchild: None,
        };

        let c = a.clone() + b.clone();
        println!("c.value: {}", c.value); // 30

        let mut d = a.clone() + b.clone();
        println!("d.value: {}", d.value); // 30

        d.value = 40;
        println!("d.value: {}", d.value); // 40

        let mut e = c * d;
        println!("e.value: {}", e.value); // 1200
        
        println!("");

        e.print_tree(false); // correct values
        println!("");
        e.print_tree(true); // all zeros
        println!("");
        e.print_tree(false); // all zeros, everything is set correctly
        println!("");

        e
    };

    dbg!(tree);
}

我手动实现了Debug,然后递归地重新实现了print_tree。我不知道是否有一种方法可以在不使用递归的情况下将 print_tree 实现为可变的,但如果您改为使用 &amp;self (删除 set_to_zero 东西),那当然是可能的。

playground

编辑:原来可以在不递归的情况下可变地迭代树值。以下代码来源于the playground in this comment by @Shepmaster

impl MyStruct {
    fn zero_tree(&mut self) {
        let mut node_stack = vec![self];
        let mut value_stack = vec![];

        // collect mutable references to each value
        while let Some(MyStruct { value, lchild, rchild }) = node_stack.pop() {
            value_stack.push(value);

            if let Some(child) = lchild {
                node_stack.push(child);
            }
            if let Some(child) = rchild {
                node_stack.push(child);
            }
        }

        // iterate over mutable references to values
        for value in value_stack {
            *value = 0;
        }
    }
}

【讨论】:

  • @aurelianmontmejat 结果证明这是可能的。查看编辑
  • 太好了,谢谢!我担心必须对非常深的树使用递归
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 2016-11-06
  • 1970-01-01
  • 2016-07-30
  • 2016-03-18
  • 2018-11-15
相关资源
最近更新 更多