rust

Pass reference of struct to multiple struct members


I need to create a struct for a shared library that is supposed to be passed out called Application. But I need substructs that access the db field and perform operations with it. There will be about 20 of those sub structs that themselves have about 20 methods each.

use infrastructure::modules::database::Database;

pub struct Application {
    db: Database,
    a_commands: ACommands,
    b_commands: BCommands,
}

impl Application {
    pub fn new(db: Database) -> Self {
        Self {
            db,
            a_commands: ACommands::new(&db),
            b_commands: BCommands::new(&db),
        }
    }
}

struct ACommands {
    db: &Database
}

impl ACommands {
    pub fn new(db: &Database) -> Self {
        Self {
            db
        }
    }
}

struct BCommands {
    db: &Database
}

impl BCommands {
    pub fn new(db: &Database) -> Self {
        Self {
            db
        }
    }
}

I get lifetime issues and borrow checkers errors. Am I going about this the right way? How else would I create and structure a public struct in a library with hundreds of functions?

Cheers.


Solution

  • Here's some example code for my earlier comment:
    Arc:

    use std::sync::Arc;
    use infrastructure::modules::database::Database;
    
    pub struct Application {
        db: Arc<Database>,
        a_commands: ACommands,
        b_commands: BCommands,
    }
    
    impl Application {
        pub fn new(db: Database) -> Self {
            let db = Arc::new(db);
            Self {
                db: db.clone(),
                a_commands: ACommands::new(db.clone()),
                b_commands: BCommands::new(db.clone()),
            }
        }
    }
    
    struct ACommands {
        db: Arc<Database>,
    }
    
    impl ACommands {
        pub fn new(db: Arc<Database>) -> Self {
            Self {
                db
            }
        }
    }
    
    struct BCommands {
        db: Arc<Database>,
    }
    
    impl BCommands {
        pub fn new(db: Arc<Database>) -> Self {
            Self {
                db
            }
        }
    }
    

    Storing Directly:

    use infrastructure::modules::database::Database;
    
    pub struct Application {
        db: Database,
        a_commands: ACommands,
        b_commands: BCommands,
    }
    
    impl Application {
        pub fn new(db: Database) -> Self {
            Self {
                db,
                a_commands: ACommands::new(),
                b_commands: BCommands::new(),
            }
        }
    
        pub fn execute_a_command(&self) {
            self.a_commands.do_something(&self.db);
        }
    
        pub fn execute_b_command(&self) {
            self.b_commands.do_something(&self.db);
        }
    }
    
    struct ACommands;
    
    impl ACommands {
        pub fn new() -> Self {
            Self
        }
    
        pub fn do_something(&self, db: &Database) {
            println!("A did something")
        }
    }
    
    struct BCommands;
    
    impl BCommands {
        pub fn new() -> Self {
            Self
        }
    
        pub fn do_something(&self, db: &Database) {
            println!("B did something")
        }
    }
    

    Arc allows multiple structs to share ownership of the Database instance, and even seamlessly works across threads, and since you're not modifying Database itself you could also use an Arc to provide read access.

    On the other hand, storing Database directly in Application and then passing a reference to it when calling methods on the sub-structs keeps the borrowing more explicit while avoiding having the sub-structs hold onto the references.