I am trying to use serde to serialize trait objects. I had a look at
How to implement `serde::Serialize` for a boxed trait object?
How can we write a generic function for checking Serde serialization and deserialization?
https://github.com/dtolnay/erased-serde
but I do not understand how to put the pieces together. Please show me how to proceed with this.
This code mimics the example in The Rust Programming Language:
use serde::Serialize; // 1.0.126
trait Paint: Serialize {
fn paint(&self) -> String;
}
#[derive(Serialize)]
struct Pen {
color: String,
}
#[derive(Serialize)]
struct Brush {
color: String,
}
impl Paint for Pen {
fn paint(&self) -> String {
return format!("Pen painting with color {}", self.color);
}
}
impl Paint for Brush {
fn paint(&self) -> String {
return format!("Brush painting with color {}", self.color);
}
}
#[derive(Serialize)]
struct Canvas {
height: f32,
width: f32,
tools: Vec<Box<dyn Paint>>,
}
impl Paint for Canvas {
fn paint(&self) -> String {
let mut s = String::new();
for tool in &self.tools {
s.push_str("\n");
s.push_str(&tool.paint());
}
return s;
}
}
fn main() {
let pen = Pen {
color: "red".to_owned(),
};
let brush = Brush {
color: "blue".to_owned(),
};
let canvas = Canvas {
height: 12.0,
width: 10.0,
tools: vec![Box::new(pen), Box::new(brush)],
};
println!("{}", canvas.paint());
serde_json::to_string(&canvas).unwrap();
}
The code does not compile due to the object-safety rules:
error[E0038]: the trait `Paint` cannot be made into an object
--> src/main.rs:33:12
|
33 | tools: Vec<Box<dyn Paint>>,
| ^^^^^^^^^^^^^^^^^^^ `Paint` cannot be made into an object
|
= help: consider moving `serialize` to another trait
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.126/src/ser/mod.rs:247:8
|
247 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
| ^^^^^^^^^ ...because method `serialize` has generic type parameters
|
::: src/main.rs:3:7
|
3 | trait Paint: Serialize {
| ----- this trait cannot be made into an object...
I understand that trait objects cannot have methods with generic parameters, but in my case I only need to serialize the Canvas struct to JSON. Is there something I could do to make that work?
The ideal serialized output would be
{
"Canvas": {
"height": 12.0,
"width": 10.0,
"tools": {
"Pen": {
"color": "red"
},
"Brush": {
"color": "blue"
}
}
}
}
Related
I would like to create a add_ten method for MyStruct which keeps track of the child that called it (as a reference).
struct MyStruct<'a> {
value: i32,
child: Option<&'a mut MyStruct<'a>>,
}
impl MyStruct<'_> {
fn add_ten(&mut self) -> MyStruct {
MyStruct {
value: self.value + 10,
child: Some(self),
}
}
}
fn main() {
let mut a = MyStruct { value: 10, child: None }; // Create some struct
let b = a.add_ten(); // Process it and return the new struct with a ref of the child who called it
println!("{}", a.value); // Be able to read the value here
println!("{}", b.value); // and here
}
However I get this error:
error: lifetime may not live long enough
--> src/main.rs:10:9
|
9 | fn add_ten(&mut self) -> MyStruct {
| ---------
| |
| let's call the lifetime of this reference `'1`
| has type `&mut MyStruct<'2>`
10 | / MyStruct {
11 | | value: self.value + 10,
12 | | child: Some(self),
13 | | }
| |_________^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
|
= note: requirement occurs because of the type `MyStruct<'_>`, which makes the generic argument `'_` invariant
= note: the struct `MyStruct<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
I understand that its a lifetime error, but I'm kind of stuck here :) I hope what I'm asking is clear enough, any help is appreciated!
EDIT
The reason I would like to use a reference, is to be able to come back to the childs and modify them. Something looking like this:
struct MyStruct<'a> {
value: i32,
child: Option<&'a mut MyStruct<'a>>,
}
impl MyStruct<'_> {
fn add_ten(&mut self) -> MyStruct {
MyStruct {
value: self.value + 10,
child: Some(self),
}
}
fn set_childs_to_zero(self) {
if self.child.is_none() { return; }
let mut child = self.child.unwrap();
loop {
child.value = 0;
if child.child.is_none() { break; }
child = child.child.unwrap();
}
}
}
fn main() {
let mut a = MyStruct { value: 10, child: None }; // Create some struct
let b = a.add_ten(); // Process it and return the new struct with a ref of the child who called it
println!("a: {}", a.value); // 10
println!("b: {}", b.value); // 20
b.set_childs_to_zero();
println!("a: {}", a.value); // 0
}
This might be a weird way of achieving what you are looking for. I doubt that it is the "right" way but hopefully someone can build off this and provide a better solution.
use std::rc::Rc;
struct MyStruct {
value: i32,
child: Option<Rc<MyStruct>>,
}
impl MyStruct {
fn add_ten(self) -> (Rc<MyStruct>, MyStruct) {
let s = Rc::new( self );
(
s.clone(),
MyStruct {
value: s.value + 10,
child: Some(s),
}
)
}
}
fn main() {
let a = MyStruct { value: 10, child: None }; // Create some struct
let (a, b) = a.add_ten(); // Process it and return the new struct with a ref of the child who called it
println!("{}", a.value); // Be able to read the value here
println!("{}", b.value); // and here
}
The use case you are describing is what Rc was built for - providing multiple ownership. You can read more about that in the documentation.
I'm confused about how to use Rust's trait objects. I have a lot of vague partial-understandings but in the end I don't understand the error I'm getting.
I am trying to implement the Command Pattern in rust in order to move a cursor around a terminal. I have it set up to send trait objects that implement a Command trait, and have the Command trait generically typed. Can I not make this trait generic?
Here's an example-ified version of my code:
pub trait Command<T> {
fn execute(&self, target_instance: &mut T);
}
pub trait Commandable<T> {
fn send(&mut self, cmd: Box<impl Command<T>>);
}
//-------------------------------------------------------
struct Cursor {
pos: (isize, isize),
}
impl Cursor {
fn move_me(&mut self, direction: (isize, isize)) {
self.pos.0 += direction.0;
self.pos.1 += direction.1;
}
}
impl Commandable<Cursor> for Cursor {
fn send(&mut self, cmd: Box<impl Command<Cursor>>) {
cmd.execute(self);
}
}
struct MoveCommand {
move_direction: (isize, isize),
}
impl MoveCommand {
pub fn new(move_direction: (isize, isize)) -> Self {
MoveCommand { move_direction }
}
}
impl Command<Cursor> for MoveCommand {
fn execute(&self, cursor: &mut Cursor) {
cursor.move_me(self.move_direction);
}
}
//------------------------------------------------------
fn handle_input(input: Option<&str>, target: &mut impl Commandable<Cursor>) {
let mut cmd: Option<Box<dyn Command<Cursor>>> = None;
if let Some(key) = input {
match key {
"K" => cmd = Some(Box::new(MoveCommand::new( (0, -1) ))),
"J" => cmd = Some(Box::new(MoveCommand::new( (0, 1) ))),
"H" => cmd = Some(Box::new(MoveCommand::new( (-1, 0) ))),
"L" => cmd = Some(Box::new(MoveCommand::new( (1, 0) ))),
_ => {}
}
}
if let Some(boxed_cmd) = cmd {
target.send(boxed_cmd); // <-----------------ERROR IS HERE
}
}
fn main() {
let mut cursor = Cursor { pos: (0, 0) };
handle_input(Some("K"), &mut cursor);
}
Here's the error:
error[E0277]: the size for values of type `dyn Command<Cursor>` cannot be known at compilation time
--> src/main.rs:54:21
|
54 | target.send(boxed_cmd);
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Command<Cursor>`
Answering my own question to help future folks:
My primary problem was that I didn't understand the difference between dyn MyTraitObj and impl MyTraitObj. I was using them interchangeably, so once I swapped all the impl's to dyn's, the code I posted compiled.
#LeoVen linked this, which solved that confusion for me: What is the difference between trait and impl trait when used as method arguments?
Thank you to all the folks that helped out.
I have a Student struct which I store in a HashSet inside a School struct wrapped by Rc references so that they can be referenced by other internal structures in School:
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
#[derive(Debug, Eq)]
struct Student {
id: usize,
// other stuff
}
impl Hash for Student {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl PartialEq for Student {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
struct School {
students: HashSet<Rc<Student>>,
// other stuff
}
impl School {
fn new() -> Self {
Self {
students: HashSet::new(),
}
}
fn add_student(&mut self) -> usize {
let id = self.students.len();
self.students.insert(Rc::new(Student { id }));
id
}
}
I wanted to implement Borrow<usize> for Rc<Student> so I could get references to students from the HashSet using their id:
use std::borrow::Borrow;
impl Borrow<usize> for Rc<Student> {
fn borrow(&self) -> &usize {
&self.id
}
}
impl School {
fn enrol(&mut self, student_id: usize) {
// Need trait Borrow<usize> implemented for Rc<Student> for this to work
if let Some(student) = self.students.get(&student_id) {
println!("Enrolling {:?}", student);
}
}
}
Unfortunately I can't do that as Borrow is defined elsewhere, and the compiler is telling me I need to create a new type.
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:26:1
|
26 | impl Borrow<usize> for Rc<Student> {
| ^^^^^-------------^^^^^-----------
| | | |
| | | `std::rc::Rc` is not defined in the current crate
| | `usize` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
I understand why a direct implementation of Borrow for Rc<T> is not possible but I'm wondering if there is a better way other than defining a new type just so I can implement Borrow. The goal is to have a shared reference but still be able to get objects from the HashSet by their id? Perhaps Rc isn't the best option here?
I'm writing very basic AI system in Rust. It's main components are:
Actions, which can be implemented by library user, for specific use,
Generic Context, which is passed to all actions, and only needs to live during the action execution,
ActionsContainer, which "globally" stores all possible actions,
System, which chooses the correct action and runs it. There are many systems, one for each agent. However, they share the same set of behaviours, so they all reference a common ActionsContainer.
Here is a minimum example which illustrates my problem.
// Generic system
trait Context {}
trait Action<C: Context> {
fn run(&self, context: &mut C);
}
struct ActionsContainer<C: Context> {
actions: Vec<Box<Action<C>>>,
}
struct System<'a, C: Context> {
actions: &'a ActionsContainer<C>,
}
impl<'a, C: Context> System<'a, C> {
fn run(&self, c: &mut C) {
self.actions.actions[0].run(c);
}
}
// Implementation
struct ContextImpl<'a> {
x: &'a i32,
y: i32,
}
impl<'a> Context for ContextImpl<'a> {}
struct ActionImpl {}
impl<'a> Action<ContextImpl<'a>> for ActionImpl {
fn run(&self, c: &mut ContextImpl) {
println!("Action!");
c.y = c.x;
}
}
// usage
fn main() {
let container = ActionsContainer {
actions: vec![Box::new(ActionImpl {})],
};
{
let system = System {
actions: &container,
};
{
let x = 8;
let mut context = ContextImpl { x: &x, y: 0 };
system.run(&context);
assert_eq!(context.y, context.x)
}
}
}
playground
The compiler complains:
error[E0309]: the parameter type `C` may not live long enough
--> src/main.rs:14:5
|
13 | struct System<'a, C: Context> {
| -- help: consider adding an explicit lifetime bound `C: 'a`...
14 | actions: &'a ActionsContainer<C>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...so that the reference type `&'a ActionsContainer<C>` does not outlive the data it points at
--> src/main.rs:14:5
|
14 | actions: &'a ActionsContainer<C>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
However, C is not stored in Action. It only needs to live while run is executing. On the other hand, the Action does need to live as long as whole System. Is there any way to annotate this?
I suspect, it has something to do with Higher-Rank Trait Bounds, but I don't see how to use them here.
I've also tried to get rid of Action as a trait object and just use plain function references:
type Action<C> = fn(&mut C);
struct ActionsContainer<C: Context> {
actions: Vec<&'static Action<C>>,
}
But the compiler error was pretty much the same.
I've found the solution:
// Generic system
trait Context {}
trait Action<C: Context> {
fn run(&self, context: &mut C);
}
struct ActionsContainer<A> {
actions: Vec<Box<A>>,
}
struct System<'a, A: 'a> {
actions: &'a ActionsContainer<A>,
}
impl<'a, A> System<'a, A> {
fn run<C>(&self, c: &mut C)
where
C: Context,
A: Action<C>,
{
self.actions.actions[0].run(c);
}
}
// Implementation
struct ContextImpl<'a> {
x: &'a i32,
y: i32,
}
impl<'a> Context for ContextImpl<'a> {}
struct ActionImpl {}
impl<'a> Action<ContextImpl<'a>> for ActionImpl {
fn run(&self, c: &mut ContextImpl) {
println!("Action!");
c.y = *c.x;
}
}
// usage
fn main() {
let container = ActionsContainer {
actions: vec![Box::new(ActionImpl {})],
};
{
let system = System {
actions: &container,
};
{
let x = 8;
let mut context = ContextImpl { x: &x, y: 0 };
system.run(&mut context);
assert_eq!(context.y, *context.x)
}
}
}
Playground
Rust always assumes that traits mentioned in generic struct will be stored in that struct (hence my lifetime problems). If you are not intending to store the trait, do not mention it in struct definition. Instead, use more general bounds, and clarify them on the method, which defines appropriate lifetime.
I am attempting to generate a Vec<&'b Color> from Vec<&'a Color>:
impl <'a> Canvas<'a> {
pub fn modify<'b>(&self, update: Update) -> Canvas<'b> {
let x = update.x;
let y = update.y;
let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
if index == self.width * y + x { update.color } else { color }
})
.collect();
Canvas { width: self.width, height: self.height, colors: colors }
}
}
However, I get the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/canvas.rs:51:50
|
51 | let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
| ^^^^
How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?
If needed, the relevant definitions are as follows:
#[derive(Debug, PartialEq, Eq)]
pub enum Color {
White,
Red,
Blue,
Green,
Purple,
Orange
}
pub struct Update<'a> {
color: &'a Color,
x: usize,
y: usize
}
pub struct Canvas<'a> {
width: usize,
height: usize,
colors: Vec<&'a Color>
}
How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?
You cannot. Lifetimes are generics, and just like it doesn't make sense to have "a Vec<T>, all but one of T is the type String, and the remaining T is the type bool", it doesn't make sense to do so with lifetimes.
What you can do is unify the lifetimes:
impl<'a> Canvas<'a> {
pub fn modify(&self, update: Update<'a>) -> Canvas<'a> {
let x = update.x;
let y = update.y;
let colors = self.colors
.iter()
.enumerate()
.map(move |(index, color)| if index == self.width * y + x {
update.color
} else {
color
})
.collect();
Canvas {
width: self.width,
height: self.height,
colors: colors,
}
}
}
Here, we've said that the update.color lifetime and the lifetime of the contained references have an intersection, and that's what the lifetime of the result will be.
Another common solution for a heterogenous collection of a fixed set of types is to introduce an enum with variants for each one:
pub enum Thing<'a, 'b> {
A(&'a Color),
B(&'b Color),
}
impl<'a> Canvas<'a> {
pub fn modify<'b>(&self, update: Update<'b>) -> Vec<Thing<'a, 'b>> {
let x = update.x;
let y = update.y;
self.colors
.iter()
.enumerate()
.map(move |(index, color)| if index == self.width * y + x {
Thing::B(update.color)
} else {
Thing::A(color)
})
.collect()
}
}