Hi, I'm pretty new to rust, having started a couple weeks ago, so if this is a dumb question, I apologize in advance. While I have made several CLA's and a web app in rust last week, I'm having difficulty getting a simple tree structure to work with Debug or Serialization. My goal is to make a drag and drop editor for "JSON Forms". I have implemented a drag and drop editor already with React using Tauri, but React desyncs from the tree, so I would like to use Rust to manage the tree in the app. Ideally, I'd like to rewrite the whole client in rust. I've managed to get several tree structure working, but I seem unable to serialize any of them.
When I try to derive Serialize/Deserialize using Serde (or Debug), I get the error:
error[E0277]: the trait bound `Vec<&'a TreeNode<'a>>: Copy` is not satisfied
--> src/main.rs:5:10
|
5 | #[derive(Serialize, Deserialize)]
| ^^^^^^^^^ the trait `Copy` is not implemented for `Vec<&'a TreeNode<'a>>`, which is required by `Cell<std::option::Option<Vec<&'a TreeNode<'a>>>>: Serialize`
...
8 | children: Cell<Option<Vec<&'a TreeNode<'a>>>>
| -------- required by a bound introduced by this call
|
Given the following code.
use std::cell::Cell;
use serde_derive::{Deserialize, Serialize};
use typed_arena::Arena;
#[derive(Serialize, Deserialize)]
struct TreeNode<'a> {
value: i32,
children: Cell<Option<Vec<&'a TreeNode<'a>>>>
}
fn main() {
let arena = Arena::new();
let root = arena.alloc(TreeNode { value: 1, children: Cell::new(None) });
let child_1 = arena.alloc(TreeNode { value: 2, children: Cell::new(None) });
let child_2 = arena.alloc(TreeNode { value: 3, children: Cell::new(None) });
root.children.set(Some(vec![child_1, child_2]));
let children = root.children.take();
for child in children.unwrap() {
println!("{}", child.value);
}
}
So the question is, how do I go about this? Am I SOL using Serde and just have to parse/walk the tree manually and write my own serialized/deserializer that clones? these vectors instead? Or is there a simpler way that I am missing.
Recursively defined data structures like TreeNode can be tricky to use in Rust because you have to think a fair bit about how to satisfy the compiler's enforcement of ownership rules on the various TreeNode references, often more than the time you spend thinking about the rest of your code (at least that's been my experience).
For me, the most natural way to construct this kind of data structure in Rust is to store all the nodes in a flat array where each node has a numeric index and you can pass around index numbers to refer to data. Numbers are easy to copy and serialize, as is the backing array.
This has nothing to do with Serde. The problem is clearly articulated by the compiler error. Cell only works with types that are Copy. You'll need RefCell instead.
Sorry, I don't meant to disagree, but just in case someone comes along after looking for the answer, this isn't it. Cell isn't restricted to only Copy types. That appears to be a misreading the documentation. The following code works as anticipated. The documentation suggest that RefCell has advantages IF you have non-copy types, but it doesn't state that you can only use Cell with entities that derive Copy. In fact, it lists the different ways that it deals with types that do not implement Copy.
Please feel free to run this code to prove this out.
use std::cell::Cell;
use serde_derive::{Deserialize, Serialize};
use typed_arena::Arena;
struct TreeNode<'a> {
value: i32,
name: String,
children: Cell<Option<Vec<&'a TreeNode<'a>>>>
}
fn main() {
let arena = Arena::new();
let root = arena.alloc(TreeNode { value: 1, name: "Bob".to_string(), children: Cell::new(None) });
let child_1 = arena.alloc(TreeNode { value: 2, name: "John".to_string(), children: Cell::new(None) });
let child_2 = arena.alloc(TreeNode { value: 3, name: "Simon".to_string(), children: Cell::new(None) });
root.children.set(Some(vec![child_1, child_2]));
let children = root.children.take();
for child in children.unwrap() {
println!("{}, {}", child.value, child.name);
}
}
The thing is: Cell::<T>::get() needs T::copy(). Serialization can't use take(), because take is destructive (it moves the inner value out of the cell by-value, replacing it with the default value).
Oh, I see what you are saying. My sincere apologies.
So I should have no issues implementing a hand written destructive serializer/deserializer, correct? And Serde does copy only, so that is why it isn't working.
Yes, you can definitely write your own destructive serializer. It'd probably be a huge footgun to make it the default in serde, but it can work in custom code if documented and encapsulated properly.