Reputation: 769
Is it possible to implement a dynamically-typed linked list in safe Rust?
The meaning of dynamically-typed linked list:
Node(56.7) -> Node("hello") -> Node(77) -> ...
I am doing this as an exercise, have tried different approaches to no avail, using Traits. Therefore doubting the possibility of doing this in Safe Rust.
Upvotes: 1
Views: 227
Reputation: 27533
You can use the Any
trait or better yet another object-safe trait which has the functionality you need from items in the list.
enum Node {
Nil,
Node {
value: Box<dyn std::any::Any>,
next: Box<Node>,
},
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Node::Nil => write!(f, "Nil"),
Node::Node { value, next } => {
if let Some(value) = value.downcast_ref::<f64>() {
write!(f, "Node({value}) -> {next:?}")
} else if let Some(value) = value.downcast_ref::<i32>() {
write!(f, "Node({value}) -> {next:?}")
} else if let Some(value) = value.downcast_ref::<&str>() {
write!(f, "Node({value:?}) -> {next:?}")
} else {
write!(f, "Node(<unknown type>) -> {next:?}")
}
}
}
}
}
fn main() {
let list = Node::Node {
value: Box::new(56.7),
next: Box::new(Node::Node {
value: Box::new("hello"),
next: Box::new(Node::Node {
value: Box::new(77),
next: Box::new(Node::Nil),
}),
}),
};
dbg!(list);
}
will output something like [src/main.rs:39] list = Node(56.7) -> Node("hello") -> Node(77) -> Nil
to stderr.
Node
has the size of two Box
es no matter the types you put into the list.downcast*
can become very awkward fast, and there isn't really much you can do with a dyn Any
otherwise.Box
.You can use an enum with every possible type:
#[derive(Debug)]
enum DynamicType {
Float(f64),
Int(i32),
Str(&'static str),
}
enum Node {
Nil,
Node {
value: DynamicType,
next: Box<Node>,
},
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Node::Nil => write!(f, "Nil"),
Node::Node { value, next } => write!(f, "Node({value:?}) -> {next:?}"),
}
}
}
fn main() {
use DynamicType::*;
let list = Node::Node {
value: Float(56.7),
next: Box::new(Node::Node {
value: Str("hello"),
next: Box::new(Node::Node {
value: Int(77),
next: Box::new(Node::Nil),
}),
}),
};
dbg!(list);
}
match
and know instantly what type you're dealing with.Node
s.DynamicType
contains a variety of sizes you might end up wasting a lot of space on Node
s.Upvotes: 3