'Rust backtrace tree structure with many mutable references
I am trying to build a data structure that is able to hold content of a evolutionary simulation. I found a bunch of nasty solutions to successively grow the tree and then enable backtracing. It works, however I am not sure if I am generating a memory leak in the backtracing part of the code.
I appended most of the code that is not the constructors etc., specifically I am unsure about Descendant::get_sequence() as I have to use unsafe code there.
Question: Is there a memory leak in Descendant::get_sequence()?
use derivative::Derivative;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Derivative)]
#[derivative(Debug)]
pub enum Haplotype {
Wildtype(Wildtype),
Descendant(Descendant),
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Wildtype {
#[derivative(Debug = "ignore")]
reference: Option<Rc<RefCell<Haplotype>>>,
sequence: Vec<u8>,
descendants: Vec<Rc<RefCell<Haplotype>>>,
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Descendant {
#[derivative(Debug = "ignore")]
reference: Option<Rc<RefCell<Haplotype>>>,
#[derivative(Debug = "ignore")]
ancestor: Rc<RefCell<Haplotype>>,
#[derivative(Debug = "ignore")]
wildtype: Rc<RefCell<Haplotype>>,
descendants: Vec<Rc<RefCell<Haplotype>>>,
position: usize,
change: u8,
}
impl Haplotype {
pub fn get_reference(&self) -> &Rc<RefCell<Haplotype>> {
let option = match self {
Haplotype::Wildtype(wt) => &wt.reference,
Haplotype::Descendant(ht) => &ht.reference,
};
match option {
Some(reference) => &reference,
None => {
eprintln!("Haplotype incorrectly initialized.");
std::process::exit(-1);
}
}
}
pub fn get_base(&self, position: usize) -> u8 {
match self {
Haplotype::Wildtype(wt) => wt.get_base(position),
Haplotype::Descendant(ht) => ht.get_base(position),
}
}
pub fn get_sequence(&self) -> Vec<u8> {
match self {
Haplotype::Wildtype(wt) => wt.get_sequence(),
Haplotype::Descendant(ht) => ht.get_sequence(),
}
}
pub fn get_length(&self) -> usize {
match self {
Haplotype::Wildtype(wt) => wt.get_length(),
Haplotype::Descendant(ht) => ht.get_length(),
}
}
}
impl Wildtype {
pub fn get_reference(&self) -> &Rc<RefCell<Haplotype>> {
match &self.reference {
Some(reference) => &reference,
None => {
eprintln!("Haplotype incorrectly initialized.");
std::process::exit(-1);
}
}
}
pub fn get_base(&self, position: usize) -> u8 {
self.sequence[position]
}
pub fn get_sequence(&self) -> Vec<u8> {
self.sequence.to_vec()
}
pub fn get_length(&self) -> usize {
self.sequence.len()
}
}
impl Descendant {
pub fn get_reference(&self) -> &Rc<RefCell<Haplotype>> {
match &self.reference {
Some(reference) => &reference,
None => {
eprintln!("Haplotype incorrectly initialized.");
std::process::exit(-1);
}
}
}
pub fn get_base(&self, position: usize) -> u8 {
if self.position == position {
return self.change;
}
self.ancestor.as_ref().borrow().get_base(position)
}
pub fn get_sequence(&self) -> Vec<u8> {
let mut sequence = vec![0; self.get_length()];
let mut current = Rc::into_raw(Rc::clone(self.get_reference()));
unsafe {
while let Haplotype::Descendant(ht) = &*(*current).borrow() {
if sequence[ht.position] == 0 {
sequence[ht.position] = ht.change
}
Rc::from_raw(current);
current = Rc::into_raw(Rc::clone(&ht.ancestor));
}
if let Haplotype::Wildtype(wt) = &*(*current).borrow() {
for (position, symbol) in wt.sequence.iter().enumerate() {
if sequence[position] == 0 {
sequence[position] = *symbol;
}
}
}
Rc::from_raw(current);
return sequence;
}
}
pub fn get_length(&self) -> usize {
self.wildtype.borrow().get_length()
}
}
Solution 1:[1]
You can restructure your code to eliminate the need of unsafe:
pub fn get_sequence(&self) -> Vec<u8> {
let mut sequence = vec![0; self.get_length()];
let mut current = Rc::clone(self.get_reference());
while let Haplotype::Descendant(ht) = &*Rc::clone(¤t).borrow() {
if sequence[ht.position] == 0 {
sequence[ht.position] = ht.change;
}
current = Rc::clone(&ht.ancestor);
}
if let Haplotype::Wildtype(wt) = &*(*current).borrow() {
for (position, symbol) in wt.sequence.iter().enumerate() {
if sequence[position] == 0 {
sequence[position] = *symbol;
}
}
}
return sequence;
}
Note that we have to borrow ht from a clone of current in the while let loop to satisfy the borrow checker, as explained here.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dogbert |
