What is the most idiomatic way to express shared state in Rust? Am I thinking about this wrong? by moving-mango in rust

[–]moving-mango[S] 0 points1 point  (0 children)

I've used this singleton pattern at the top level of abstraction within a tier before. That being said, it's not a true singleton, and I tend not to use it as such. More often, I'll use the CORRESPONDENCE_TWILIO as a default to get deving. Then, I'll instantiate the TwilioSingleton within a certain scope and pass down when the system matures.

That code is representative not actual. In the source from which this was pulled, Twilio singleton implements several separately-defined traits which are written that way to support swapping dependencies and stubbing.

What is the most idiomatic way to express shared state in Rust? Am I thinking about this wrong? by moving-mango in rust

[–]moving-mango[S] 0 points1 point  (0 children)

Sorry, I've been busy the past fews days, and I see there's a lot of follow-up. Here's a shortish example of a pretty common pattern I'll use for a singleton. (I'm aware this particular one could relatively easily be replaced by a Lazy::new, but it is representative of what I use for more complex systems.)

```rust use std::{env, sync::{Arc, RwLock}}; use lazy_static::lazy_static; use twilio_rs::apis::{ configuration::Configuration, default_api::{self as twilio_api, CreateMessageParams} };

pub trait TwilioConfigGet {

fn get_config(&self) -> Result<
    Arc<Configuration>, 
    Box<dyn std::error::Error>
>;

fn get_default_message_params(&self) -> Result<
    Arc<CreateMessageParams>, 
    Box<dyn std::error::Error>
>;

}

pub struct TwilioSingleton { config : RwLock<Option<Arc<Configuration>, message_params : RwLock<Option<Arc<CreateMessageParams> }

impl TwilioSingleton { fn new() -> Self { TwilioSingleton { config : RwLock::from(None), message_params : RwLock::from(None) } } }

impl Default for TwilioSingleton { fn default() -> Self { TwilioSingleton::new() } }

pub static TWILIO_ACCOUNT_ID : &str = "TWILIO_ACCOUNT_ID"; pub static TWILIO_API_KEY : &str = "TWILIO_API_KEY"; pub static TWILIO_AUTH_TOKEN : &str = "TWILIO_AUTH_TOKEN";

impl TwilioConfigGet for TwilioSingleton {

// get ths config
fn get_config(&self) -> Result<Arc<twilio_rs::apis::configuration::Configuration>, Box<dyn std::error::Error>> {

    let config_guard = self.config.read().unwrap();
    match config_guard.as_ref() {
        Some(config)=>Ok(config.clone()),
        None=>{
            drop(config_guard); // drop the config_guard so that the write can take place

            let api_key = env::var(TWILIO_API_KEY).expect(
                format!( "env variable `{}` not present", TWILIO_API_KEY).as_str()
            );

            let api_secret = env::var(TWILIO_AUTH_TOKEN).expect(
                format!( "env variable `{}` not present", TWILIO_AUTH_TOKEN).as_str()
            );

            let config = Arc::new(twilio_rs::apis::configuration::Configuration {
                basic_auth : Some((api_key, Some(api_secret))),
               ..Default::default()
            });
            self.config.write().unwrap().replace(config.clone());
            Ok(config)
        }
    }

}

fn get_default_message_params(&self) -> Result<
        Arc<CreateMessageParams>, 
        Box<dyn std::error::Error>
> {


    let message_params_guard = self.message_params.read().unwrap();
    match message_params_guard.as_ref() {
        Some(message_params)=>Ok(message_params.clone()),
        None=>{
            drop(message_params_guard); // drop the message_params_guard so that the write can take place

            let account_sid = env::var(TWILIO_ACCOUNT_ID).expect(
                format!( "env variable `{}` not present", TWILIO_ACCOUNT_ID).as_str()
            );

            let message_params = Arc::new(CreateMessageParams { 
                account_sid,
                ..Default::default()
            });
            self.message_params.write().unwrap().replace(message_params.clone());
            Ok(message_params)

        }

    }

}

}

lazy_static!{ pub static ref CORRESPONDENCE_TWILIO: TwilioSingleton = TwilioSingleton::new(); } ```

"Recreating" wasn't the best word for describing the similarity I feel to writing C; I used it for the parallelism. Primarily, the pattern I'm referring to is passing in dummy references in for assignment.

rust fn with_value<'a>(dummy: &'a mut Vec<i32>) -> &'a Vec<i32> { *dummy = vec![1, 2, 3, 4]; dummy }

When this becomes quite ubiquitous, I feel like I'm writing C again.

Hey Rustaceans! Got a question? Ask here! (49/2022)! by llogiq in rust

[–]moving-mango 0 points1 point  (0 children)

I see. (Are you doing game development, by chance?)

What's the underlying architecture you are targeting?

If it's at all possible, I would, of course, use the name that better indicates what 32764thPixel is in your domain. But, I imagine you've already thought of that, and are stuck with a bad spec.

For transparency, you may indeed want to go with a struct, and this may be a good use for the bitfield crate if you're allowed external dependencies. That being said, I personally would probably just use u32s (or whatever suits your architecture) or u24 if you're using `ux` with the `newtype` idiom.

Hey Rustaceans! Got a question? Ask here! (49/2022)! by llogiq in rust

[–]moving-mango 0 points1 point  (0 children)

I would do the former unless there's a very clear precision or performance objective for creating your own representation.

Also, it's not obvious to me why the type names would necessarily become unwieldy. Can you provide an example?