Heterogeneous collection as a member of a class in Rust - 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);
}

Related

Value referencing data owned by the current function

Here's my code:
struct Something<'a> {
val: u32,
another: &'a AnotherThing,
}
struct AnotherThing {
val: u32,
}
impl Default for AnotherThing {
fn default() -> Self {
Self {
val: 2,
}
}
}
trait Anything {
fn new(val: u32) -> Self;
}
impl Anything for Something<'_> {
fn new(val: u32) -> Self {
Self {
val,
another: &AnotherThing::default(),
}
}
}
fn main() {
let _ = Something::new(1);
}
It doesn't compile because:
Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:24:9
|
24 | / Self {
25 | | val,
26 | | another: &AnotherThing::default(),
| | ----------------------- temporary value created here
27 | | }
| |_________^ returns a value referencing data owned by the current function
I understand the problem but I don't know how to fix it. If it's not possible to use the Default trait for this case, how can I deal with the function ownership. Below a simpler example:
struct Something<'a> {
val: u32,
another: &'a AnotherThing,
}
struct AnotherThing {
val: u32,
}
trait Anything {
fn new(val: u32) -> Self;
}
impl Anything for Something<'_> {
fn new(val: u32) -> Self {
let at = AnotherThing { val : 2 };
Self {
val,
another: &at,
}
}
}
fn main() {
let _ = Something::new(1);
}
If I had another: &AnotherThing { val : 2 } instead of another: &at it would work. If I want the another attribute to be a reference and get the value from a function, how can I do it?
You can do like this
#[derive(Default)]
struct Something<'a> {
val: u32,
another: &'a AnotherThing,
}
struct AnotherThing {
val: u32,
}
impl<'a> Default for &'a AnotherThing {
fn default() -> &'a AnotherThing {
&AnotherThing {
val: 3,
}
}
}
trait Anything {
fn new(val: u32) -> Self;
}
impl Anything for Something<'_> {
fn new(val: u32) -> Self {
Self {
val,
..Default::default()
}
}
}
Another option is to create a const item, of which you can create a reference with 'static lifetime, thus binding to any 'a:
struct Something<'a> {
val: u32,
another: &'a AnotherThing,
}
struct AnotherThing {
val: u32,
}
const ANOTHER_THING_DEFAULT: AnotherThing = AnotherThing { val: 3 };
trait Anything {
fn new(val: u32) -> Self;
}
impl Anything for Something<'_> {
fn new(val: u32) -> Self {
Self {
val,
another: &ANOTHER_THING_DEFAULT,
}
}
}

Optional trait's method: call empty method or skip it at all?

If I have an optional method that has to be called many many times what is better if I want to skip it: have an empty body and call it or check the bool/Option before calling it?
The following benchmark make no sense. It gave zeroes.
#![feature(test)]
extern crate test;
trait OptTrait: 'static {
fn cheap_call(&mut self, data: u8);
fn expensive_call(&mut self, data: u8);
}
type ExpensiveFnOf<T> = &'static dyn Fn(&mut T, u8);
struct Container<T: OptTrait> {
inner: T,
expensive_fn: Option<ExpensiveFnOf<T>>,
}
impl<T: OptTrait> Container<T> {
fn new(inner: T, expensive: bool) -> Self {
let expensive_fn = {
if expensive {
Some(&T::expensive_call as ExpensiveFnOf<T>)
} else {
None
}
};
Self {
inner,
expensive_fn,
}
}
}
struct MyStruct;
impl OptTrait for MyStruct {
fn cheap_call(&mut self, _data: u8) {
}
fn expensive_call(&mut self, _data: u8) {
}
}
#[cfg(test)]
mod tests {
use super::*;
use test::Bencher;
#[bench]
fn bench_always_call_empty(b: &mut Bencher) {
let mut cont = Container::new(MyStruct, false);
b.iter(|| {
cont.inner.cheap_call(0);
cont.inner.expensive_call(1);
});
}
#[bench]
fn bench_alwaws_skip_empty(b: &mut Bencher) {
let mut cont = Container::new(MyStruct, false);
b.iter(|| {
cont.inner.cheap_call(0);
if let Some(func) = cont.expensive_fn {
func(&mut cont.inner, 1);
}
});
}
}

How to create a single threaded singleton in Rust?

I'm currently trying to wrap a C library in rust that has a few requirements. The C library can only be run on a single thread, and can only be initialized / cleaned up once on the same thread. I want something something like the following.
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
// This line doesn't work.
static mut CTX: Option<(ThreadId, Rc<Context>)> = None;
struct Context(*mut c_void);
impl Context {
fn acquire() -> Result<Rc<Context>, Error> {
// If CTX has a reference on the current thread, clone and return it.
// Otherwise initialize the library and set CTX.
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { cleanup_lib(self.0); }
}
}
Anyone have a good way to achieve something like this? Every solution I try to come up with involves creating a Mutex / Arc and making the Context type Send and Sync which I don't want as I want it to remain single threaded.
A working solution I came up with was to just implement the reference counting myself, removing the need for Rc entirely.
#![feature(once_cell)]
use std::{error::Error, ffi::c_void, fmt, lazy::SyncLazy, sync::Mutex, thread::ThreadId};
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
#[derive(Debug)]
pub enum ContextError {
InitOnOtherThread,
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ContextError::InitOnOtherThread => {
write!(f, "Context already initialized on a different thread")
}
}
}
}
impl Error for ContextError {}
struct StaticPtr(*mut c_void);
unsafe impl Send for StaticPtr {}
static CTX: SyncLazy<Mutex<Option<(ThreadId, usize, StaticPtr)>>> =
SyncLazy::new(|| Mutex::new(None));
pub struct Context(*mut c_void);
impl Context {
pub fn acquire() -> Result<Context, ContextError> {
let mut ctx = CTX.lock().unwrap();
if let Some((id, ref_count, ptr)) = ctx.as_mut() {
if *id == std::thread::current().id() {
*ref_count += 1;
return Ok(Context(ptr.0));
}
Err(ContextError::InitOnOtherThread)
} else {
let ptr = unsafe { init_lib() };
*ctx = Some((std::thread::current().id(), 1, StaticPtr(ptr)));
Ok(Context(ptr))
}
}
}
impl Drop for Context {
fn drop(&mut self) {
let mut ctx = CTX.lock().unwrap();
let (_, ref_count, ptr) = ctx.as_mut().unwrap();
*ref_count -= 1;
if *ref_count == 0 {
unsafe {
cleanup_lib(ptr.0);
}
*ctx = None;
}
}
}
I think the most 'rustic' way to do this is with std::sync::mpsc::sync_channel and an enum describing library operations.
The only public-facing elements of this module are launch_lib(), the SafeLibRef struct (but not its internals), and the pub fn that are part of the impl SafeLibRef.
Also, this example strongly represents the philosophy that the best way to deal with global state is to not have any.
I have played fast and loose with the Result::unwrap() calls. It would be more responsible to handle error conditions better.
use std::sync::{ atomic::{ AtomicBool, Ordering }, mpsc::{ SyncSender, Receiver, sync_channel } };
use std::ffi::c_void;
extern "C" {
fn init_lib() -> *mut c_void;
fn do_op_1(ctx: *mut c_void, a: u16, b: u32, c: u64) -> f64;
fn do_op_2(ctx: *mut c_void, a: f64) -> bool;
fn cleanup_lib(ctx: *mut c_void);
}
enum LibOperation {
Op1(u16,u32,u64,SyncSender<f64>),
Op2(f64, SyncSender<bool>),
Terminate(SyncSender<()>),
}
#[derive(Clone)]
pub struct SafeLibRef(SyncSender<LibOperation>);
fn lib_thread(rx: Receiver<LibOperation>) {
static LIB_INITIALIZED: AtomicBool = AtomicBool::new(false);
if LIB_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() {
panic!("Tried to double-initialize library!");
}
let libptr = unsafe { init_lib() };
loop {
let op = rx.recv();
if op.is_err() {
unsafe { cleanup_lib(libptr) };
break;
}
match op.unwrap() {
LibOperation::Op1(a,b,c,tx_res) => {
let res: f64 = unsafe { do_op_1(libptr, a, b, c) };
tx_res.send(res).unwrap();
},
LibOperation::Op2(a, tx_res) => {
let res: bool = unsafe { do_op_2(libptr, a) };
tx_res.send(res).unwrap();
}
LibOperation::Terminate(tx_res) => {
unsafe { cleanup_lib(libptr) };
tx_res.send(()).unwrap();
break;
}
}
}
}
/// This needs to be called no more than once.
/// The resulting SafeLibRef can be cloned and passed around.
pub fn launch_lib() -> SafeLibRef {
let (tx,rx) = sync_channel(0);
std::thread::spawn(|| lib_thread(rx));
SafeLibRef(tx)
}
// This is the interface that most of your code will use
impl SafeLibRef {
pub fn op_1(&self, a: u16, b: u32, c: u64) -> f64 {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op1(a, b, c, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn op_2(&self, a: f64) -> bool {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op2(a, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn terminate(&self) {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Terminate(res_tx)).unwrap();
res_rx.recv().unwrap();
}
}

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.

How do I downgrade an Rc<RefCell<T>> into a Weak<T>?

After adding a RefCell to the inside struct, I don't know how to have a reference to only the Rc value.
In other words, I'm creating an Rc<RefCell<T>> but I only need the Rc<T> to be able to downgrade into a Weak.
use std::{
cell::RefCell,
fmt,
rc::{Rc, Weak},
};
#[derive(Debug)]
struct Field {
i: u8,
y: u8,
}
impl Field {
pub fn new() -> Self {
Self { i: 8, y: 6 }
}
}
#[derive(Debug)]
pub struct Parent {
field_1: Field,
child: RefCell<Option<Child>>,
}
impl Parent {
pub fn new() -> Rc<RefCell<Self>> {
let n = Rc::new(RefCell::new(Self {
field_1: Field::new(),
child: RefCell::new(None),
}));
*n.borrow_mut().child.borrow_mut() = Some(Child::new(&n));
n
}
pub fn modify(&mut self) {
self.field_1.i = 9;
}
pub fn to_string(&self) -> String {
format!(
"{:?} {}",
self.field_1,
self.child.borrow().as_ref().unwrap()
)
}
}
#[derive(Debug)]
pub struct Child {
parent: Weak<Parent>,
field_2: Field,
}
impl Child {
pub fn new(parent: &Rc<Parent>) -> Self {
Self {
parent: Rc::downgrade(parent),
field_2: Field::new(),
}
}
}
impl fmt::Display for Child {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.parent.upgrade().unwrap().field_1.i == 1 {
write!(f, "set: {:?}", self.field_2)
} else {
write!(f, "not set {:?}", self.field_2)
}
}
}
fn main() {
let mut parent = Parent::new();
parent.borrow_mut().modify();
println!("{}", parent.borrow().to_string());
}
Error:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:32:62
|
32 | *n.borrow_mut().child.borrow_mut() = Some(Child::new(&n));
| ^^ expected struct `Parent`, found struct `std::cell::RefCell`
|
= note: expected reference `&std::rc::Rc<Parent>`
found reference `&std::rc::Rc<std::cell::RefCell<Parent>>`
I realize that the data structure itself might be the problem, but I currently can't think of a improvement.

Resources