Runtime implementation feature selection (Factory vs Shapeshifter struct) - rust

Imagine the following (common) task at hand: a cross-platform library is needed which provides access to physical devices (e.g. USB ones) and shall expose a unified API across all platforms. The following additional properties are to be satisfied:
A platform can support multiple backends to interact with a physical device.
The backend can be chosen by consumers of the library by using URIs.
One must be able to choose the backend at runtime.
Here is the common implementation part representing two concrete USB device abstractions, "A" and "B":
// only available if feature "A" is selected
struct A {}
// only available if feature "B" is selected
struct B {}
trait Common {
fn doit(&self);
}
impl Common for A {
fn doit(&self) { /* do something */ }
}
impl Common for B {
fn doit(&self) { /* do something */ }
}
The following is a naive example of using a global context struct as "factory" to solve the task:
struct Context {}
impl Context {
pub fn create<S: AsRef<str>>(_uri: S) -> Option<Box<dyn Common>> {
#[cfg(feature = "A")]
if _uri == "A" {
return Some(Box::new(A {}))
}
#[cfg(feature = "B")]
if _uri == "B" {
return Some(Box::new(B {}))
}
None
}
}
fn main() {
// pretend feature "A" has been selected
let a = Context::create("A");
}
But there is another way to do this in Rust - using a single struct which hides the implementation details but acts as a single point of contact to consumers:
struct Device {
inner: Box<dyn Common>,
}
impl Device {
pub fn new<S: AsRef<str>>(_uri: S) -> Option<Self> {
#[cfg(feature = "A")]
if _uri == "A" {
return Some(Device {
inner: Box::new(A {})
});
}
#[cfg(feature = "B")]
if _uri == "B" {
return Some(Device {
inner: Box::new(B {})
});
}
None
}
}
impl Common for Device {
fn doit(&self) { self.inner.doit() }
}
fn main() {
// pretend feature "A" has been selected
let a = Device::new("A");
}
Which one is deemed more idiomatic in Rust?
What about auto-derived traits such as Send or Sync?
For example, "A" and "B" being Send does not make Box<dyn Common> Sync automatically, instead one has to explicitly add the constraint: Box<dyn Common + Send>.
Adding such trait constraints in the first case (factory style) requires changing the function signature. In the second case, one only needs to change the internals of struct Device, the public facing API is not affected - apart from struct Device becoming e.g. Send.

Related

How to create a derived column for my struct?

#[derive(Serialize, Deserialize, Debug)]
struct Product {
id: usize,
name: String,
timestamp: i128
}
I deserialize this struct value from a JSON value.
Now I want to expose another property on my struct:
dt: OffsetDateTime
I want this property to be immutable, and set only once. So I don't want to expose a function that like below b/c it would re-calculate each time I call it:
impl Product {
fn dt(&self) -> OffsetDateTime {
OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
}
In java world or other languages I would do something like this:
private dt: OffsetDateTime = null;
public OffsetDateTime getDt() {
if(dt == null) {
dt = OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
return dt;
}
Does Rust have a similar pattern I can use?
You have three options:
Initialize it when initializing the struct, by providing a constructor. This is by far the easiest solution, if initialization isn't expensive or access is common enough that initializing always is not a problem. This is not equivalent to your Java code, however.
Store an Option<OffsetDateTime> and use Option::get_or_insert_with() to initialize it on access. This is cheapier than the third option, but requires a &mut access:
pub fn dt(&mut self) -> &OffsetDateTime {
self.dt.get_or_insert_with(|| { /* Initialization logic */ })
}
Use a library such as once_cell (or the unstable versions in std) to initialize under & access. You can use either Sync or not, depending on whether you need multiple threads to access the data):
pub fn dt(&self) -> &OffsetDateTime {
self.dt.get_or_init(|| { /* Initialization logic */ })
}
You could use an Option to simulate the Java behavior.
struct P {
pub thing: Option<i32>
}
impl P {
pub fn calc_thing( mut self ) -> i32 {
if let None = self.thing {
self.thing = Some(5);
}
self.thing.unwrap()
}
}
fn main(){
let p = P{ thing: None };
println!( "{}", p.calc_thing() );
}

Implementing a simple state machine with no dynamic allocation and compile time assertion

I'm thinking about a state machine that forces me to implement all state transitions at compile time, so no dynamic allocation is used. I'd like to do
let machine = RtspMachine::begin();
machine.event(...);
machine.event(...);
and event would change the internal state of the machine.
Here's my sketch:
struct Init {}
struct Ready{}
struct Playing{}
struct Recording{}
struct _RtspState<T> {
t: T
}
type RtspState<T> = _RtspState<T>;
trait StateChange where Self: Sized {
fn from<T>(self, state: &RtspState<T>, event: &Event) -> std::result::Result<RtspMachine, EventError>;
}
impl StateChange for RtspMachine {
fn from(self, state: RtspState<Init>, event: &Event) -> std::result::Result<RtspMachine, EventError> {
//...
}
fn from(self, state: RtspState<Init>, event: &Event) -> std::result::Result<RtspMachine, EventError> {
//...
}
//...
}
pub(crate) struct RtspMachine {
state: RtspState
}
The problem is that in order to ensure in compile time that I implemented all transitions, the RtspState must be generic, so I can match over its types. But then, the RtspMachine would have to be generic, thus I'd not be able to simply do machine.event to modify its internal state because its type would change on a state transition.
I thought of doing
enum RtspState {
Init,
Ready,
Playing,
Recording,
}
but then I cannot match over the state, because RtspState::Init is not a type, but a variant.
One solution would be to make a enum RtspMachineWrapper:
enum RtspMachineWrapper {
RtspMachine<Init>,
RtspMachine<Ready>,
RtspMachine<Playing>,
RtspMachine<Recording>
}
but then I'd have to reimplement every RtspMachine call to RtspMachineWrapper by doing a large match over all states.
What should I do?
It looks to me like you're confusing two slightly different patterns that are often used with state machines.
The first is to ensure that every transition is handled nicely by encapsulating the FSM in an enum.
enum RtspState {
Init,
Ready,
Playing,
Recording,
}
impl RtspState {
pub fn on_event(&self, event: &Event) -> std::result::Result<Self, EventError> {
match self {
Init => Ok(Ready),
Ready => if event.foo { Ok(Playing) } else { Err(EventError::new("bang")) },
...
// The compiler will complain if we miss any.
}
}
}
pub fn main() -> EventError {
let state = RtspState::Init;
let state = state.on_event(an_event());
let state = state.on_event(foo_event());
...
}
The other pattern is to create a FSM where invalid events are impossible at compile time. This doesn't use an enum, but uses separate structs as you have in your example. The difference is that only limited events are supported by each struct type.
struct Init {}
struct Ready {}
struct Playing {}
struct Recording {}
struct AnEvent {}
struct FooEvent {}
impl Init {
pub fn on_an_event(&self, e: &AnEvent) -> Ready {
Ready
}
}
impl Ready {
pub fn on_foo_event(&self, e: &FooEvent) -> Playing {
Playing
}
}
pub fn main() {
let state = Init;
let state = state.on_an_event(an_event());
let state = state.on_foo_event(foo_event());
// The compiler will complain if we try to do an invalid event.
}
The only way to do it on stack is to use enum.
You can skip useless variants of enum while matching:
match my_enum_value {
MyEnum::Value1(state) => { /* do something */ },
MyEnum::Value2(state) => { /* do something else */ },
_ => { /* do nothing? */ },
}

What is the Rust equivalent of C++'s shared_from_this?

I have an object that I know that is inside an Arc because all the instances are always Arced. I would like to be able to pass a cloned Arc of myself in a function call. The thing I am calling will call me back later on other threads.
In C++, there is a standard mixin called enable_shared_from_this. It enables me to do exactly this
class Bus : public std::enable_shared_from_this<Bus>
{
....
void SetupDevice(Device device,...)
{
device->Attach(shared_from_this());
}
}
If this object is not under shared_ptr management (the closest C++ has to Arc) then this will fail at run time.
I cannot find an equivalent.
EDIT:
Here is an example of why its needed. I have a timerqueue library. It allows a client to request an arbitrary closure to be run at some point in the future. The code is run on a dedicated thread. To use it you must pass a closure of the function you want to be executed later.
use std::time::{Duration, Instant};
use timerqueue::*;
use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};
// inline me keeper cos not on github
pub struct MeKeeper<T> {
them: Mutex<Weak<T>>,
}
impl<T> MeKeeper<T> {
pub fn new() -> Self {
Self {
them: Mutex::new(Weak::new()),
}
}
pub fn save(&self, arc: &Arc<T>) {
*self.them.lock().deref_mut() = Arc::downgrade(arc);
}
pub fn get(&self) -> Arc<T> {
match self.them.lock().upgrade() {
Some(arc) => return arc,
None => unreachable!(),
}
}
}
// -----------------------------------
struct Test {
data:String,
me: MeKeeper<Self>,
}
impl Test {
pub fn new() -> Arc<Test>{
let arc = Arc::new(Self {
me: MeKeeper::new(),
data: "Yo".to_string()
});
arc.me.save(&arc);
arc
}
fn task(&self) {
println!("{}", self.data);
}
// in real use case the TQ and a ton of other status data is passed in the new call for Test
// to keep things simple here the 'container' passes tq as an arg
pub fn do_stuff(&self, tq: &TimerQueue) {
// stuff includes a async task that must be done in 1 second
//.....
let me = self.me.get().clone();
tq.queue(
Box::new(move || me.task()),
"x".to_string(),
Instant::now() + Duration::from_millis(1000),
);
}
}
fn main() {
// in real case (PDP11 emulator) there is a Bus class owning tons of objects thats
// alive for the whole duration
let tq = Arc::new(TimerQueue::new());
let test = Test::new();
test.do_stuff(&*tq);
// just to keep everything alive while we wait
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
}
cargo toml
[package]
name = "tqclient"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
timerqueue = { git = "https://github.com/pm100/timerqueue.git" }
parking_lot = "0.11"
There is no way to go from a &self to the Arc that self is stored in. This is because:
Rust references have additional assumptions compared to C++ references that would make such a conversion undefined behavior.
Rust's implementation of Arc does not even expose the information necessary to determine whether self is stored in an Arc or not.
Luckily, there is an alternative approach. Instead of creating a &self to the value inside the Arc, and passing that to the method, pass the Arc directly to the method that needs to access it. You can do that like this:
use std::sync::Arc;
struct Shared {
field: String,
}
impl Shared {
fn print_field(self: Arc<Self>) {
let clone: Arc<Shared> = self.clone();
println!("{}", clone.field);
}
}
Then the print_field function can only be called on an Shared encapsulated in an Arc.
having found that I needed this three times in recent days I decided to stop trying to come up with other designs. Maybe poor data design as far as rust is concerned but I needed it.
Works by changing the new function of the types using it to return an Arc rather than a raw self. All my objects are arced anyway, before they were arced by the caller, now its forced.
mini util library called mekeeper
use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};
pub struct MeKeeper<T> {
them: Mutex<Weak<T>>,
}
impl<T> MeKeeper<T> {
pub fn new() -> Self {
Self {
them: Mutex::new(Weak::new()),
}
}
pub fn save(&self, arc: &Arc<T>) {
*self.them.lock().deref_mut() = Arc::downgrade(arc);
}
pub fn get(&self) -> Arc<T> {
match self.them.lock().upgrade() {
Some(arc) => return arc,
None => unreachable!(),
}
}
}
to use it
pub struct Test {
me: MeKeeper<Self>,
foo:i8,
}
impl Test {
pub fn new() -> Arc<Self> {
let arc = Arc::new(Test {
me: MeKeeper::new(),
foo:42
});
arc.me.save(&arc);
arc
}
}
now when an instance of Test wants to call a function that requires it to pass in an Arc it does:
fn nargle(){
let me = me.get();
Ooddle::fertang(me,42);// fertang needs an Arc<T>
}
the weak use is what the shared_from_this does so as to prevent refcount deadlocks, I stole that idea.
The unreachable path is safe because the only place that can call MeKeeper::get is the instance of T (Test here) that owns it and that call can only happen if the T instance is alive. Hence no none return from weak::upgrade

How do I set a static lifetime to a returned tuple in Rust?

Assume I have the following constructor that returns a tuple:
pub struct WebCam {
id: u8
}
impl WebCam {
fn new() -> (Self, bool) {
let w = WebCam {id: 1 as u8};
return (w, false);
}
}
pub fn main() {
static (cam, isRunning): (WebCam, bool) = WebCam::new();
}
The above code does not compile. However, if I change static to let it compiles just fine. I'm not sure how to set the lifetime on the individual values of the returned tuple (in one line)?
https://doc.rust-lang.org/reference/items/static-items.html
It can be confusing whether or not you should use a constant item or a static item. Constants should, in general, be preferred over statics unless one of the following are true:
Large amounts of data are being stored
The single-address property of statics is required.
Interior mutability is required.
A static item is similar to a constant, except that it represents a precise memory location in the program. All references to the static refer to the same memory location. Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Non-mut static items that contain a type that is not interior mutable may be placed in read-only memory. Static items do not call drop at the end of the program.
All access to a static is safe, but there are a number of restrictions on statics:
The type must have the Sync trait bound to allow thread-safe access.
Statics allow using paths to statics in the constant expression used to initialize them, but statics may not refer to other statics by value, only through a reference.
Constants cannot refer to statics.
I would rewrite you code like this:
pub struct WebCam {
id: u8,
}
impl WebCam {
fn new() -> (Self, bool) {
(WebCam { id: 1u8 }, false)
}
}
pub fn main() {
let (cam, is_running) = WebCam::new();
println!("{} {}", cam.id, is_running);
}
This works too:
pub struct WebCam {
id: u8,
}
impl WebCam {
fn new() -> (Self, bool) {
(WebCam { id: 1u8 }, false)
}
}
static mut IS_RUNNING: bool = false;
static mut WEB_CAM: WebCam = WebCam { id: 0u8 };
pub fn main() {
let (cam, is_running) = WebCam::new();
unsafe {
IS_RUNNING = is_running;
WEB_CAM.id = cam.id;
}
println!("{} {}", cam.id, is_running);
}

How best to deal with struct field that can change types

I'm working with a library that uses Rust types to keep track of state. As a simplified example, say you have two structs:
struct FirstStruct {}
struct SecondStruct {}
impl FirstStruct {
pub fn new() -> FirstStruct {
FirstStruct {}
}
pub fn second(self) -> SecondStruct {
SecondStruct {}
}
// configuration methods defined in this struct
}
impl SecondStruct {
pub fn print_something(&self) {
println!("something");
}
pub fn first(self) -> FirstStruct {
FirstStruct {}
}
}
And to actually use these structs you usually follow a pattern like so, after printing you may stay in second state or go back to first state depending on how you're using the library:
fn main() {
let first = FirstStruct::new();
let second = first.second(); // consumes first
second.print_something();
// go back to default state
let _first = second.first();
}
I want to create my own struct that handles the state changes internally and simplifies the interface. This also lets me have a single mutable reference around that I can pass to other functions and call the print method. Using it should look something like this:
fn main() {
let mut combined = CombinedStruct::new(FirstStruct::new());
combined.print();
}
I've come up with the following solution that works, at least in this simplified example:
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
struct CombinedStruct {
state: Option<StructState>,
}
impl CombinedStruct {
pub fn new(first: FirstStruct) -> CombinedStruct {
CombinedStruct {
state: Some(StructState::First(first)),
}
}
pub fn print(&mut self) {
let s = match self.state.take() {
Some(s) => match s {
StructState::First(first) => first.second(),
StructState::Second(second) => second,
},
None => panic!(),
};
s.print_something();
// If I forget to do this, then I lose access to my struct
// and next call will panic
self.state = Some(StructState::First(s.first()));
}
}
I'm still pretty new to Rust but this doesn't look right to me. I'm not sure if there's a concept I'm missing that could simplify this or if this solution could lead to ownership problems as my application gets more complicated. Is there a better way to do this?
Playground link
I once had a similar problem and went basically with your solution, but I avoided the Option.
I.e. I basically kept your
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
If an operation tries to convert a FirstStruct to a SecondStruct, I introduced a function try_to_second roughly as follows:
impl StructState {
fn try_to_second(self) -> Result<SecondState, StructState> {
/// implementation
}
}
In this case, an Err indicates that the StructState has not been converted to SecondStruct and preserves the status quo, while an Ok value indicates successfull conversion.
As an alternative, you could try to define try_to_second on FirstStruct:
impl FirstStruct {
fn try_to_second(self) -> Result<FirstStruct, SecondStruct> {
/// implementation
}
}
Again, Err/Ok denote failure/success, but in this case, you have more concrete information encoded in the type.

Resources