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()
}
}
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.