How to reuse common codes for structs? - rust

I want to reuse codes for structs. For example:
use std::fmt::Display;
struct CommonStruct<T: Display> {
// could have more fields
data: T
}
struct A<T: Display> {
com: CommonStruct<T>,
age: i32
}
struct B<T: Display> {
com: CommonStruct<T>,
name: String
}
impl<T: Display> A<T> {
// could be more common functions
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.com.data);
}
}
impl<T: Display> B<T> {
// could be more common functions
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.com.data);
}
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.print_data();
}
where A and B have some common fields packed by CommonStruct and some common functions (e.g., print_data).
I tried to use trait but cannot figure out a solution:
use std::fmt::Display;
struct CommonStruct<T: Display> {
// could have more fields
data: T
}
struct A<T: Display> {
com: CommonStruct<T>,
age: i32
}
struct B<T: Display> {
com: CommonStruct<T>,
name: String
}
trait Common {
// could be more common functions
fn print_data(&self) {
print_data(&self)
}
}
impl<T: Display> Common for A<T> {
}
impl<T: Display> Common for B<T> {
}
fn print_data(t: &Common) {
// could be more complicated
println!("data: {}", t.com.data);
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.print_data();
}

Since print_data only uses the CommonStruct, and A and B share no other fields, make it an implementation of CommonStruct and call it directly.
impl <T: Display> CommonStruct<T> {
fn print_data(&self) {
println!("data: {}", self.data);
}
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.com.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.com.print_data();
}
Alternatively, make a trait which has a concrete implementation of print_data which relies on a method to get the data.
trait HasData<T: Display> {
fn get_data(&self) -> &T;
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.get_data());
}
}
Then each only has to implement how to get the data.
impl<T: Display> HasData<T> for CommonStruct<T> {
fn get_data(&self) -> &T {
return &self.data;
}
}
impl<T: Display> HasData<T> for A<T> {
fn get_data(&self) -> &T {
return &self.com.data;
}
}
impl<T: Display> HasData<T> for B<T> {
fn get_data(&self) -> &T {
return &self.com.data;
}
}
fn main() {
let a = A{ com: CommonStruct{data: 1}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 2}, name: "123".to_string() };
b.print_data();
let c = CommonStruct{data: 3};
c.print_data();
}

I saw that you wanted to use traits. I've included a working example below.
However, traits are used to implement shared behavior. A trait must be implemented on each type you wish to have the shared behavior. So, you do have to impl a separate print_data function for a given type.
use std::fmt::Display;
// `Common` structure of some type `T`.
struct Common<T: Display> {
data: T,
}
// `Printable` trait used to print `data`.
trait Printable {
fn print_data(&self);
}
struct A {
common: Common<String>,
}
impl A {
fn new(s: &str) -> A {
A {
common: Common { data: s.to_owned() },
}
}
}
// Implement `Printable the trait for A`.
impl Printable for A {
fn print_data(&self) {
println!("A.common.data: {}", self.common.data)
}
}
struct B {
common: Common<i32>,
}
// Implement the `Printable` trait for `B`.
impl Printable for B {
fn print_data(&self) {
println!("B.common.data: {}", self.common.data)
}
}
So that's traits, but if you must call the same function to print the data, then maybe something like the following can work for you. It defines an enum with three variants. You can then match on a particular variant as demonstrated by print_all_data.
use std::path::PathBuf;
struct G {
path: PathBuf,
common: Common<String>,
}
enum C {
D(A),
E(B),
F(G),
}
fn print_all_data(c: C) {
match c {
C::D(a) => println!("{}", a.common.data),
C::E(b) => println!("{}", b.common.data),
C::F(g) => println!("{} {:?}", g.common.data, g.path)
}
}
fn main() {
let a = A::new("hello");
a.print_data();
let b = B {
common: Common { data: 42 },
};
b.print_data();
let g = G {
path: PathBuf::from("some/path/file.txt"),
common: Common {data: "some_data".to_owned()}
};
let cfg = C::F(g);
print_all_data(cfg);
print_all_data(C::D(a));
print_all_data(C::E(b));
}

Related

How can i pass a struct method as callback in Rust?

In C++, I work with this.
class A{
public:
void read();
}
class B{
public:
void setCallback(std::function<void()> f){
...
}
}
int main(){
A a;
B b;
b.setCallback(std::bind(&A::read,&a));
}
Could I implement the same function in Rust?
Does this work for you?
struct A {
x: u32,
}
impl A {
fn do_something(&mut self) {
self.x += 1;
}
}
struct B<F> {
callback: Option<F>,
}
impl<F: FnMut()> B<F> {
fn set_callback(&mut self, callback: F) {
self.callback = Some(callback);
}
fn call_callback(&mut self) {
self.callback.as_mut().map(|cb| cb());
}
}
fn main() {
let mut a = A { x: 0, };
let mut b = B { callback: None, };
b.set_callback(|| a.do_something());
b.call_callback();
assert_eq!(a.x, 1);
}
I took a conservative guess at what the mutability requirements would be.
#isaactfa answer is correct, but std::function uses dynamic dispatch and is more like Box<dyn Fn()> than using generics.
struct B<'a> {
callback: Option<Box<dyn FnMut() + 'a>>,
}
impl<'a> B<'a> {
fn set_callback(&mut self, callback: Box<dyn FnMut() + 'a>) {
self.callback = Some(callback);
}
fn call_callback(&mut self) {
self.callback.as_mut().map(|cb| cb());
}
}
fn main() {
let mut a = A { x: 0 };
let mut b = B { callback: None };
b.set_callback(Box::new(|| a.do_something()));
b.call_callback();
drop(b); // It is necessary for the code to not use `a` while it's borrowed by the closure
assert_eq!(a.x, 1);
}
Playground.
You can have a little more convenience by creating the Box within the struct methods, so the caller doesn't have to create it:
impl<'a> B<'a> {
fn set_callback(&mut self, callback: impl FnMut() + 'a) {
self.callback = Some(Box::new(callback));
}
}
Playground.

Heterogeneous collection as a member of a class in Rust

I am new to Rust, and does not fully understand lifetime, so probably, that is why I can't solv the following issue. I need a solution in which a class has a heterogeneous HashMap containing different objects derived from the same trait.
I have to be able to extend an object with some (multiple) functionality dinamically. Other solutions are also welcome. Adding functionality to the class in compile time could also work, but adding functionality directly to the main class not.
use std::collections::HashMap;
trait DoerTrait {
fn do_something( & self, a : u8, b : u8 ) -> u8;
}
struct MyDoer<'a> {
}
impl DoerTrait for MyDoer<'a> {
fn do_something( & self, a : u8, b : u8 ) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers : HashMap<u8,&'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers : HashMap::new()
}
}
fn add_doer( &mut self, id : u8, doer : & dyn DoerTrait ) {
self.doers.insert( id, doer );
}
fn do_something( & self, id : u8 ) {
match self.doers.get( &id ) {
Some( doer ) => {
println!( "{}", doer(19,26) );
}
None => {
println!( "Doer not specified!" );
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer{};
mymain.add_doer( 42, &mydoer );
mymain.do_something( 42 );
}
Not too sure what issue you have, once MyDoer has been stripped of its incorrect (unnecessary) lifetime and the lifetime has correctly been declared on impl MyMain, the compiler directly points to the parameter of add_doer not matching (after which it points out that doer in do_something is not a function):
use std::collections::HashMap;
trait DoerTrait {
fn do_something(&self, a: u8, b: u8) -> u8;
}
struct MyDoer;
impl DoerTrait for MyDoer {
fn do_something(&self, a: u8, b: u8) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers: HashMap<u8, &'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers: HashMap::new(),
}
}
fn add_doer(&mut self, id: u8, doer: &'a dyn DoerTrait) {
self.doers.insert(id, doer);
}
fn do_something(&self, id: u8) {
match self.doers.get(&id) {
Some(doer) => {
println!("{}", doer.do_something(19, 26));
}
None => {
println!("Doer not specified!");
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer {};
mymain.add_doer(42, &mydoer);
mymain.do_something(42);
}

Return TypeId of Self from a default trait method

I am implementing my own blocking pub/sub pattern as seen below.
use std::any::{Any, TypeId};
#[derive(Clone)]
struct ModelA {
pub id: u32
}
#[derive(Clone)]
struct ModelB {
pub id: u32
}
struct ModelASubscriberOne<'a> {
pub model: &'a ModelA
}
impl<'a> Subscriber<u32> for ModelASubscriberOne<'a> {
fn get_type(&self) -> TypeId {
TypeId::of::<ModelASubscriberOne>()
}
fn execute(&self) {
println!("SubscriberOne ModelA id: {}", self.model.id);
}
fn id(&self) -> u32 {
self.model.id
}
}
struct ModelASubscriberTwo<'a> {
pub model: &'a ModelA
}
impl<'a> Subscriber<u32> for ModelASubscriberTwo<'a> {
fn get_type(&self) -> TypeId {
TypeId::of::<ModelASubscriberTwo>()
}
fn execute(&self) {
println!("SubscriberTwo ModelA id: {}", self.model.id);
}
fn id(&self) -> u32 {
self.model.id
}
}
trait Subscriber<TKey:Eq + 'static> {
fn get_type(&self) -> TypeId;
fn execute(&self);
fn id(&self) -> TKey;
fn filter(&self, other: TKey) -> bool{
self.id() == other
}
}
struct Publisher<'a> {
pub subscribers: Vec<&'a dyn Subscriber<u32>>
}
impl<'a> Publisher<'a> {
fn new() -> Publisher<'a> {
Publisher{subscribers: Vec::new()}
}
fn subscribe(&mut self, subscriber: &'a dyn Subscriber<u32>) {
self.subscribers.push(subscriber);
}
fn publish<T>(&self) where T: 'static + Subscriber<u32> {
self.subscribers.iter().filter(|x| {
TypeId::of::<T>() == x.get_type()
})
.for_each(|x| {
x.execute();
});
}
fn unsubscribe<T:Any>(&mut self, subscriber: u32) {
let position = self.subscribers.iter().position(|x| {
TypeId::of::<T>() == x.get_type() && x.filter(subscriber)
});
match position {
Some(position) => {
self.subscribers.remove(position);
},
_ => {}
}
}
}
fn main() {
let model_a = ModelA{id:0};
let mut model_a_list: Vec<ModelA> = Vec::new();
model_a_list.push(model_a);
let model_a = ModelA{id:1};
model_a_list.push(model_a);
let mut publisher = Publisher::new();
let subscriber = ModelASubscriberOne{model: &model_a_list[0]};
publisher.subscribe(&subscriber);
let subscriber = ModelASubscriberOne{model: &model_a_list[1]};
publisher.subscribe(&subscriber);
let subscriber = ModelASubscriberTwo{model: &model_a_list[1]};
publisher.subscribe(&subscriber);
println!("Subscribed");
publisher.publish::<ModelASubscriberOne>();
publisher.publish::<ModelASubscriberTwo>();
publisher.unsubscribe::<ModelASubscriberOne>(model_a_list[1].id);
println!("Unsubscribed");
publisher.publish::<ModelASubscriberOne>();
publisher.publish::<ModelASubscriberTwo>();
}
I feel like it would be helpful to be able to have a default implementation of get_type() for Subscriber that returns a TypeId for Self. Is there a way to do this?
I tried the following:
trait Subscriber<TKey:Eq + 'static> {
fn get_type(self: &Self) -> TypeId {
TypeId::of::<Self>()
}
// <...>
}
I get the following message:
error[E0310]: the parameter type `Self` may not live long enough
--> src\main.rs:51:5
|
51 | TypeId::of::<Self>()
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `Self: 'static`...
= note: ...so that the type `Self` will meet its required lifetime bounds
I am not sure where to put the lifetime bound. I expect I need an Any trait bound as well but I am not sure where to put that either.
Thanks in advance.

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 to create a null pointer in a struct?

How do I create null pointer properties in struct like in C++? I do not quite understand how to make a pointer to an empty space in the memory.
C++
struct test{
string data;
assocc *el;
}
srtuct assocc {
T *data;
}
Rust
struct test {
data: String,
ponts: *const Option<assocc<T>>,
}
struct assocc<T> {
data: T,
}
impl test {
fn new(data: String) -> test {
test {
data: data,
ponts: None,/*This don't work*/
}
}
}
impl<T> assocc<T> {
fn new(data: T) -> assocc {
assocc {
data: data,
}
}
}
There are a few issues with your code. To start out, Rust type names are generally CamelCased, so let's start with that:
struct Test {
data: String,
ponts: *const Option<Assocc<T>>,
}
struct Assocc<T> {
data: T,
}
Next, because Test is enclosing something with a generic type T, it itself needs to be generic:
struct Test<T> {
data: String,
ponts: *const Option<Assocc<T>>,
}
struct Assocc<T> {
data: T,
}
Unless you're doing FFI where you absolutely need to store a raw pointer, it's probably better to just have your Test struct own the Assoc:
struct Test<T> {
data: String,
ponts: Option<Assocc<T>>,
}
struct Assocc<T> {
data: T,
}
And some of the types in the impl blocks need to be changed around to add some generics:
impl<T> Test<T> {
fn new(data: String) -> Test<T> {
Test {
data: data,
points: None,
}
}
}
impl<T> Assocc<T> {
fn new(data: T) -> Assocc<T> {
Assocc { data: data }
}
}
Finally, let's add a main function to ensure that we can actually use the structs as we're expecting:
struct Test<T> {
data: String,
ponts: Option<Assocc<T>>,
}
struct Assocc<T> {
data: T,
}
impl<T> Test<T> {
fn new(data: String) -> Test<T> {
Test {
data: data,
ponts: None,
}
}
}
impl<T> Assocc<T> {
fn new(data: T) -> Assocc<T> {
Assocc { data: data }
}
}
fn main() {
let mut t = Test::new("Hello!".to_string());
t.ponts = Some(Assocc::new(32));
}
You can use std::ptr::null:
struct Test<T> {
data: String,
ponts: *const Option<Assocc<T>>,
}
struct Assocc<T> {
data: T,
}
impl<T> Test<T> {
fn new(data: String) -> Test<T> {
Test {
data: data,
ponts: std::ptr::null(),
}
}
}
impl<T> Assocc<T> {
fn new(data: T) -> Assocc<T> {
Assocc {
data: data,
}
}
}
But are you sure you want to use raw pointers in Rust? In Rust, we mostly use references.
A more idiomatic way to do this in Rust is the following:
struct Test<'a, T: 'a> {
data: String,
ponts: Option<&'a Assocc<T>>,
}
impl<'a, T> Test<'a, T> {
fn new(data: String) -> Test<'a, T> {
Test {
data: data,
ponts: None,
}
}
}

Resources