How can I test an object's other-trait functions - rust

I have a Template struct implementing a encoder function that returns a reference to a Boxed Encoder.
I also have a FixedEncoder struct that implements Encoder
I can create the Template and get the Encoder out, but how do I test the functions of FixedEncoder? I'm only looking to get FixedEncoder for testing purposes, so "unsafe" solutions are fine (though safe ones are preferred)
In my following example I get the error
error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope
Example (playground):
pub struct Template {
encoder: Box<Encoder>
}
impl Template {
fn new(encoder: Box<Encoder>) -> Template {
Template { encoder }
}
fn encoder(&self) -> &Box<Encoder> {
&self.encoder
}
}
pub trait Encoder {
fn isEncoder(&self) -> bool {
true
}
}
pub struct FixedEncoder {
length: usize
}
impl FixedEncoder {
pub fn new(length: usize) -> FixedEncoder {
FixedEncoder { length }
}
pub fn length(&self) -> usize {
self.length
}
}
impl Encoder for FixedEncoder {}
fn main() {
let fixed_encoder = FixedEncoder::new(1);
let template = Template::new(Box::new(fixed_encoder));
assert_eq!(template.encoder().isEncoder(), true); // works
assert_eq!(&template.encoder().length(), 1); // error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope
}

I was able to accomplish this by using Any.
Add an as_any declaration to Encoder
Add an as_any function to FixedEncoder
Use .as_any().downcast_ref().unwrap() on the retreived Encoder
playground
use std::any::Any;
pub struct Template {
encoder: Box<Encoder>
}
impl Template {
fn new(encoder: Box<Encoder>) -> Template{
Template {
encoder
}
}
fn encoder(&self) -> &Box<Encoder> {
&self.encoder
}
}
pub trait Encoder {
fn isEncoder(&self) -> bool {
true
}
fn as_any(&self) -> &dyn Any;
}
pub struct FixedEncoder {
length: usize
}
impl FixedEncoder {
pub fn new(length: usize) -> FixedEncoder {
FixedEncoder { length }
}
pub fn length(&self) -> usize {
self.length
}
}
impl Encoder for FixedEncoder {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let fixed_encoder = FixedEncoder::new(1);
let template = Template::new(Box::new(fixed_encoder));
assert_eq!(template.encoder().isEncoder(), true); // works
let fixed_encoder_from_template : &FixedEncoder = &template.encoder().as_any().downcast_ref().unwrap();
assert_eq!(&fixed_encoder_from_template.length, &(1 as usize));
}

Related

How to use the typestate pattern in other struct

I want to use the typestate pattern to define several states that allow some exclusive operations on each of them.
I'm using traits instead of an enum to allow further customizations.
So, I'm able to use this pattern until I try to include it inside a struct (the Session part) that is mutated when files are added, changed or removed.
trait IssueState {}
struct Open;
impl IssueState for Open {}
struct WIP {
elapsed_time: u32,
}
impl IssueState for WIP {}
struct Closed {
elapsed_time: u32,
}
impl IssueState for Closed {}
struct Issue<T: IssueState + ?Sized> {
state: Box<T>,
comments: Vec<String>,
}
impl<T: IssueState> Issue<T> {
pub fn comment<S: Into<String>>(&mut self, comment: S) -> &mut Self {
self.comments.push(comment.into());
self
}
}
impl Issue<Open> {
pub fn new() -> Self {
Self {
state: Box::new(Open),
comments: vec![],
}
}
pub fn start(self) -> Issue<WIP> {
Issue {
state: Box::new(WIP { elapsed_time: 0 }),
comments: self.comments,
}
}
}
impl Issue<WIP> {
pub fn work(&mut self, time: u32) -> &mut Self {
self.state.elapsed_time += time;
self
}
pub fn done(self) -> Issue<Closed> {
let elapsed_time = self.state.elapsed_time;
Issue {
state: Box::new(Closed { elapsed_time }),
comments: self.comments,
}
}
}
impl Issue<Closed> {
pub fn elapsed(&self) -> u32 {
self.state.elapsed_time
}
}
struct Session<T: IssueState> {
user: String,
current_issue: Issue<T>,
}
impl<T: IssueState> Session<T> {
pub fn new<S: Into<String>>(user: S, issue: Issue<T>) -> Self {
Self {
user: user.into(),
current_issue: issue,
}
}
pub fn comment<S: Into<String>>(&mut self, comment: S) {
self.current_issue.comment(comment);
}
}
impl Session<WIP> {
pub fn work(&mut self, time: u32) {
self.current_issue.work(time);
}
}
trait Watcher {
fn watch_file_create(&mut self);
fn watch_file_change(&mut self);
fn watch_file_delete(&mut self);
}
impl<T: IssueState> Watcher for Session<T> {
fn watch_file_create(&mut self) {
self.current_issue = Issue::<Open>::new();
}
fn watch_file_change(&mut self) {}
fn watch_file_delete(&mut self) {}
}
fn main() {
let open = Issue::<Open>::new();
let mut wip = open.start();
wip.work(10).work(30).work(60);
let closed = wip.done();
println!("Elapsed {}", closed.elapsed());
let mut session = Session::new("Reviewer", closed);
session.comment("It is OK");
session.watch_file_create();
}
Rust Playground (original)
Rust Playground (edited)
What can I do to fix the problems?
Is the typestate pattern limited to only some situations that do not depend a lot on external events? I mean, I'm trying to use it for processing events, but is it a dead end?, why?
Your Session has a Issue<dyn IssueState> member, but you want to implement its work method by calling Issue<WIP>'s work method. The compiler complains, because an Issue<dyn IssueState> is not (necessarily) a Issue<WIP> and so does not implement that method.

How to pass on closures through multiple sctructs and functions

I have a hierarchy of structs where I need to call a method in the topmost struct from an Iterator::next implementation at the lowest level.
Current implementation is as follows:
Functional abstract:
pub struct TopLevel {
answer: usize,
}
pub struct MidLevelIter<'mli> {
count: usize,
top_level: &'mli TopLevel,
}
pub struct MidLevel<'ml> {
top_level: &'ml TopLevel,
}
pub struct LowestLevelIter<'lli> {
count: usize,
top_level: &'lli TopLevel,
}
impl TopLevel {
pub fn new() -> Self {
Self { answer: 42 }
}
pub fn iter(&self) -> MidLevelIter<'_> {
MidLevelIter {
count: 1,
top_level: self,
}
}
fn calculate(&self, _: usize) -> &usize {
&self.answer
}
}
impl<'mli> Iterator for MidLevelIter<'mli> {
type Item = MidLevel<'mli>;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 2 {
self.count = 2;
Some(MidLevel {
top_level: self.top_level,
})
} else {
None
}
}
}
impl<'lli> Iterator for LowestLevelIter<'lli> {
type Item = &'lli usize;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 2 {
self.count = 2;
Some(self.top_level.calculate(self.count))
} else {
None
}
}
}
impl<'ml> MidLevel<'ml> {
pub fn iter(&self) -> LowestLevelIter<'ml> {
LowestLevelIter {
count: 1,
top_level: self.top_level,
}
}
}
fn main() {
let collector = TopLevel::new();
for pc in collector.iter() {
for sc in pc.iter() {
println!("SC={}", sc);
}
}
}
This works fine, but it kind of bothers me that I have to pass a reference to TopLevel through all these structs.
So, my idea was to pass only the required method as a closure. That way, the lower levels need not to know anything about the TopLevel construct.
the following approach, however, fails because of "cannot move out of self.mapper which is behind a mutable reference".
pub struct TopLevel {
answer: usize,
}
pub struct MidLevelIter<'mli> {
count: usize,
mapper: Box<dyn Fn(usize) -> &'mli usize + 'mli>,
}
pub struct MidLevel<'ml> {
mapper: Box<dyn Fn(usize) -> &'ml usize + 'ml>,
}
pub struct LowestLevelIter<'lli> {
count: usize,
mapper: Box<dyn Fn(usize) -> &'lli usize + 'lli>,
}
impl TopLevel {
pub fn new() -> Self {
Self { answer: 42 }
}
pub fn iter(&self) -> MidLevelIter<'_> {
MidLevelIter {
count: 1,
mapper: Box::new(self.mapper()),
}
}
fn calculate(&self, _: usize) -> &usize {
&self.answer
}
fn mapper<'m>(&'m self) -> impl Fn(usize) -> &'m usize {
move |secondary_index| self.calculate(secondary_index)
}
}
impl<'mli> Iterator for MidLevelIter<'mli> {
type Item = MidLevel<'mli>;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 2 {
self.count = 2;
Some(MidLevel {
mapper: self.mapper,
})
} else {
None
}
}
}
impl<'lli> Iterator for LowestLevelIter<'lli> {
type Item = &'lli usize;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 2 {
self.count = 2;
Some((self.mapper)(self.count))
} else {
None
}
}
}
impl<'ml> MidLevel<'ml> {
pub fn iter(&self) -> LowestLevelIter<'ml> {
LowestLevelIter {
count: 1,
mapper: self.mapper,
}
}
}
fn main() {
let collector = TopLevel::new();
for pc in collector.iter() {
for sc in pc.iter() {
println!("SC={}", sc);
}
}
}
Although I can understand what the compiler tells me there, I don't see how to circumvent it.
Traits are the answer to my problem.
Basically, on the lowest level I just wanted to perform a transformation of some sort on the items of the iteration. Although closures looked suitable for that, Rust provides another feature to accomplish this: Traits.
On the lowest level, I want to convert a numerical index to a key reference. So make a trait for it:
trait IndexToKey {
fn calculate(&self, _: usize) -> &usize;
}
This trait can now be passed on, e.g.:
pub struct MidLevelIter<'mli> {
count: usize,
mapper: &'mli dyn IndexToKey,
}
Originally, my TopLevel struct provided the logic, so let's implement the trait:
impl IndexToKey for TopLevel {
fn calculate(&self, _ix: usize) -> &usize {
&self.answer
}
}
Now we can pass a reference to trait implementation down to the lowest level, which now simply performs the conversion:
impl<'lli> Iterator for LowestLevelIter<'lli> {
type Item = &'lli usize;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 2 {
self.count = 2;
Some(self.mapper.calculate(self.count))
} else {
None
}
}
}
No lifetime issues, no disclosure or dependency on implementation details of the TopLevel structs at the other levels.
Implementation on the Rust playground

macro_rules!() error: Fix for "the usage of `my_macro!` is likely invalid in impl item context"?

Playground link
I have several different structs grouped together in an enum:
pub enum Ty {
A(AStruct),
B(BStruct)
}
pub struct AStruct {
base: BaseStruct
}
impl AStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
pub struct BStruct {
base: BaseStruct
}
impl BStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
All of these types will share a base struct that is common to them all. This base struct will have a lot of methods that I don't want to duplicate for each supertype.
pub struct BaseStruct {
x: i32
}
impl BaseStruct {
pub fn new(x: i32) -> Self {
Self {
x
}
}
pub fn get_x(&self) -> i32 {
self.x
}
}
#[macro_export]
macro_rules! base_struct_passthrough_impls {
() => {
pub fn get_x(&self) -> i32 {
self.base.get_x()
};
}
}
However, trying to use this code results in the following error:
error: macro expansion ignores token `;` and any following
--> src/main.rs:37:10
|
37 | };
| ^
...
46 | base_struct_passthrough_impls!();
| --------------------------------- caused by the macro expansion here
|
= note: the usage of `base_struct_passthrough_impls!` is likely invalid in impl item context
It seems like macro_rules!() is not usable in the impl item context. Is this correct, and if so is there anyway around this restriction? Would a proc macro work here, or would doing something like this work better?
The issue isn't the macro usage, but the definition. You included a trailing semicolon after the function definition created by the macro, which is what causes the error. If you remove it, everything works fine - here's the code:
fn main() {
let types = vec![Ty::A(AStruct::new(32)), Ty::B(BStruct::new(64))];
types.iter().for_each(|item| {
dbg!(match item {
Ty::A(a_struct) => a_struct.get_x(),
Ty::B(b_struct) => b_struct.get_x(),
});
})
}
pub enum Ty {
A(AStruct),
B(BStruct)
}
pub struct BaseStruct {
x: i32
}
impl BaseStruct {
pub fn new(x: i32) -> Self {
Self {
x
}
}
pub fn get_x(&self) -> i32 {
self.x
}
}
#[macro_export]
macro_rules! base_struct_passthrough_impls {
() => {
pub fn get_x(&self) -> i32 {
self.base.get_x()
} // there was an illegal semicolon here
}
}
pub struct AStruct {
base: BaseStruct
}
impl AStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
pub struct BStruct {
base: BaseStruct
}
impl BStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}

Collection with Traits that return Self

I'm trying to have a collection of objects that implement a particular trait.
If I use a trait that returns a value, this works
use std::collections::BTreeMap;
struct World {
entities: Vec<usize>,
database: BTreeMap<usize, Box<ReadValue>>,
//database : BTreeMap<usize,Box<ReadEcs>>, // Doesn't work
}
struct SourceInputGateway {
entity_id: usize,
}
trait ReadValue {
fn read(&self) -> f32;
}
impl ReadValue for SourceInputGateway {
fn read(&self) -> f32 {
0.0
}
}
But if I want to return Self as a value then this doesn't work, either as a method template param or associated type
trait ReadEcs {
type T;
fn read(&self) -> &Self::T;
}
impl ReadEcs for SourceInputGateway {
type T = SourceInputGateway;
fn read(&self) -> &Self::T {
self
}
}
What I would like to do is have a map of types that implement ReadEcs, the concrete type of which is not important.
Further clarification edit
If I expand the example by adding
// Different sized type
struct ComputeCalculator {
entity_id : usize,
name : String,
}
impl ReadValue for ComputeCalculator {
fn read(&self) -> f32 {
1230.0
}
}
then I can do this
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_write() {
let mut world = World::new();
world.database.insert(0,Box::new(SourceInputGateway{ entity_id : 1}));
world.database.insert(2,Box::new(ComputeCalculator{ entity_id : 2 , name : "foo".into() }));
for (k,ref v) in world.database {
let item : &Box<ReadValue> = v;
item.read();
}
}
}
but if I change or add a trait method that returns Self, I can't do this.
I'd like to understand a way to get around it without unsafe pointers.
I thought about this a little more and I think it's possible to solve this while retaining all the advantages of type safety and contiguous storage.
Defining a entity manager with pointers to storage
struct Entities {
entities: Vec<usize>,
containers: Vec<Box<Storage>>,
}
The components themselves that store data related to behaviour
struct Position {
entity_id: usize,
position: f32,
}
struct Velocity {
entity_id: usize,
velocity: f32,
}
We will have many instances of components, so we need a contiguous memory store, accessed by an index.
struct PositionStore {
storage: Vec<Position>,
}
struct VelocityStore {
storage: Vec<Velocity>,
}
trait Storage {
// Create and delete instances of the component
fn allocate(&mut self, entity_id: usize) -> usize;
// Interface methods that would correspond to a base class in C++
fn do_this(&self);
// fn do_that(&self);
}
The trait for the store implements an arena style storage plus the methods it would pass to to the components. This would likely be in the "System" part of an ECS but is left as an exercise for later.
I would like a hint as to how to pass a Fn() that constructs a custom object to the allocate() method. I've not figured that out yet.
impl Storage for PositionStore {
fn allocate(&mut self, entity_id: usize) -> usize {
self.storage.push(Position {
entity_id,
position: 0.0,
});
self.storage.len() - 1
}
fn run(&self) {
self.storage.iter().for_each(|item| { println!("{}",item.position); });
}
}
impl Storage for VelocityStore {
fn allocate(&mut self, entity_id: usize) -> usize {
self.storage.push(Velocity {
entity_id,
velocity: 0.0,
});
self.storage.len() - 1
}
fn do_this(&self) {
self.storage.iter().for_each(|item| { println!("{}",item.velocity); });
}
}
Some boiler plate.
impl Default for PositionStore {
fn default() -> PositionStore {
PositionStore {
storage: Vec::new(),
}
}
}
impl Default for VelocityStore {
fn default() -> VelocityStore {
VelocityStore {
storage: Vec::new(),
}
}
}
I think this could be thought about a little more. It stores the relationship between the storage of the components and their position
Instead of T::default(), you may want to pass a lambda function that has a specific initialisation for each of your components
impl Entities {
fn register<T>(&mut self) -> usize
where
T: Storage + Default + 'static,
{
self.containers.push(Box::new(T::default()));
self.containers.len() - 1
}
fn create<T>(&mut self, entity_id: usize, id: usize) -> usize {
self.containers[id].allocate(entity_id)
}
fn run_loop(&self) {
self.containers.iter().for_each(|x| x.do_this());
}
}
A test case to see if this works
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arbitary() {
let mut entities = Entities {
entities: Vec::new(),
containers: Vec::new(),
};
let velocity_store_id = entities.register::<VelocityStore>();
let position_store_id = entities.register::<PositionStore>();
let _ = entities.create::<Velocity>(123, velocity_store_id);
let _ = entities.create::<Velocity>(234, velocity_store_id);
let _ = entities.create::<Position>(234, position_store_id);
let _ = entities.create::<Position>(567, position_store_id);
entities.run_loop();
}
}

How can I construct a PlasmaContainsRequest if I don't know how to get a WIPOffset object?

How can I construct a PlasmaContainsRequest object, since I don't know how to get a WIPOffset object to construct a PlasmaContainsRequestArgs object?
I used flatc 1.10.0 to generate this Rust code:
impl<'a> PlasmaContainsRequest<'a> {
#[inline]
pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
PlasmaContainsRequest { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
args: &'args PlasmaContainsRequestArgs<'args>,
) -> flatbuffers::WIPOffset<PlasmaContainsRequest<'bldr>> {
let mut builder = PlasmaContainsRequestBuilder::new(_fbb);
if let Some(x) = args.object_id {
builder.add_object_id(x);
}
builder.finish()
}
pub const VT_OBJECT_ID: flatbuffers::VOffsetT = 4;
#[inline]
pub fn object_id(&self) -> Option<&'a str> {
self._tab
.get::<flatbuffers::ForwardsUOffset<&str>>(PlasmaContainsRequest::VT_OBJECT_ID, None)
}
}
pub struct PlasmaContainsRequestArgs<'a> {
pub object_id: Option<flatbuffers::WIPOffset<&'a str>>,
}
impl<'a> Default for PlasmaContainsRequestArgs<'a> {
#[inline]
fn default() -> Self {
PlasmaContainsRequestArgs { object_id: None }
}
}

Resources