'm lost!
I'm learning Rust. And along that way, I need a doupble linked list. shrug I know! Don't do that! But I need to.
This program can't do it at compile time. The setup how structs are connected and even which ones are actually used is determined by a configuration file.
I have tried for a week now to find a solution. I have read that "double linked list" article (that didn't help me).
I have read this article How not to learn Rust and got even more upset and angry.
But I'm stubborn.
So here is my take:
use std::rc::Rc;
pub type DCFloat = f64;
trait Linkage {
fn set_left(&mut self, left: Rc<Box<dyn Linkage>>);
fn set_right(&mut self, right: Rc<Box<dyn Linkage>>);
fn call_left(&self);
fn get_value(&self) -> DCFloat;
}
struct Part1 {
my_left: Option<Rc<Box<dyn Linkage>>>,
my_right: Option<Rc<Box<dyn Linkage>>>,
some_int: i32,
}
impl Part1 {
fn new() -> Self {
return Part1 {
my_left: None,
my_right: None,
some_int: 42,
};
}
}
impl Linkage for Part1 {
fn set_left(&mut self, left: Rc<Box<dyn Linkage>>) {
self.my_left = Some(left);
}
fn set_right(&mut self, right: Rc<Box<dyn Linkage>>) {
self.my_right = Some(right);
}
fn call_left(&self) {
if let Some(my_left) = self.my_left.as_ref() {
let f = my_left.get_value();
println!("Part1: got {f}");
} else {
println!("Part1: no left!");
}
}
fn get_value(&self) -> DCFloat {
println!("Part1: returning 1.1");
1.1
}
}
/// Following Part2 is the same as Part1, just the name changed.
/// Part2 will have additional functions outside of the trait. Left out here for simplicity
struct Part2 {
my_left: Option<Rc<Box<dyn Linkage>>>,
my_right: Option<Rc<Box<dyn Linkage>>>,
some_float: f32,
}
impl Part2 {
fn new() -> Self {
return Part2 {
my_left: None,
my_right: None,
some_float: 47.11,
};
}
}
impl Linkage for Part2 {
fn set_left(&mut self, left: Rc<Box<dyn Linkage>>) {
self.my_left = Some(left);
}
fn set_right(&mut self, right: Rc<Box<dyn Linkage>>) {
self.my_right = Some(right);
}
fn call_left(&self) {
if let Some(my_left) = self.my_left.as_ref() {
let f = my_left.get_value();
println!("Part2: got {f}");
} else {
println!("Part2: no left!");
}
}
fn get_value(&self) -> DCFloat {
println!("Part2: returning 2.2");
2.2
}
}
/* ====================================================================== */
fn main() {
let mut p1 = Rc::new(Box::new(Part1::new()));
let mut p2 = Rc::new(Box::new(Part2::new()));
p1.set_left(p2);
// ~~
// rustc: mismatched types
// expected struct `Rc<Box<(dyn Linkage + 'static)>>`
// found struct `Rc<Box<Part2>>`
// `Part2` implements `Linkage` so you could box the found value and coerce it to
// the trait object `Box<dyn Linkage>`, you will have to change the expected type as well
p2.set_right(p1);
p1.call_left();
}
At the end, all my hopes break apart with the message "rustc: mismatched types". Starting with "Part2 implements ..." I only hear gibberisch and can't come up with a fix. Part2 does implement trait linkage. So please, my sweet rustc, use it!
I can't come up with a different solution to avoid linked lists that do communicate with each other. Some kind of proxy that knows who follows whom only ends with other back-and-forth-references.
Yes, I have asked a similar question already and implemented the answer. But alas, didn't try it out completely. Trying to set the link back ended in a double borrow. I do understand the reasons why Rust is so picky about that. But reference counting and boxes should fix that. Right?
TIA for any help.
PS: I tried it with brute force and unsafe { std::ptr::copy(...) }, but Rust was stronger.