Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to avoid cloning `self` when calling multiple struct method in rust?

i was trying to call two struct methods, namely self.bootstrap() and self.run(), inside self.main(). However, i got a compiler warning for the self.run() , said use of moved value: self, value used here after move

i believe the issue is because self.bootstrap() has taken ownership of self, and not returning it. thus, main no longer has the ownership of self, causing the ownership issue of self.run()

the FailedServer struct and implementation is a minimal reproducible example of this error

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

mod minimal_example {
    use std::{net::TcpStream, sync::Arc};

    struct FailedServer {
        nodes: Vec<String>,
    }

    impl FailedServer {
        pub fn main(self: Arc<Self>) -> Result<(), Box<dyn std::error::Error>> {
            self.bootstrap();

            // the below `self.run()` with this compile error
            // use of moved value: `self`
            // value used here after move
            self.run() // <- compiler error
        }

        fn bootstrap(self: Arc<Self>) {
            let nodes = self.nodes.clone();
            let len = nodes.len();
            for i in 0..len {
                let node = nodes[i].clone();
                std::thread::spawn(move || {
                    TcpStream::connect(node);
                });
            }
        }

        fn run(self: Arc<Self>) -> Result<(), Box<dyn std::error::Error>> {
            loop {
                // a blocking loop to do something
            }

            Ok(())
        }
    }
}

i have tried to clone self inside main before calling self.bootstrap() in the example below and it works. But it seems weird to me to do as it would imply that if i have to call 10 different struct methods inside main, i would have to clone self multiple times to keep the ownership inside main.

so my question is, is it actually the right way to do or it is my skill issue.

below is the working example

mod minimal_example {
    use std::{net::TcpStream, sync::Arc};

    struct WorkableServer {
        nodes: Vec<String>,
    }

    impl WorkableServer {
        pub fn start(self: Arc<Self>) -> Result<(), Box<dyn std::error::Error>> {
            self.clone().bootstrap();

            self.run()
        }

        fn bootstrap(self: Arc<Self>) {
            let nodes = self.nodes.clone();
            let len = nodes.len();
            for i in 0..len {
                let node = nodes[i].clone();
                println!("Bootstrapping to node: {}", node);
                std::thread::spawn(move || {
                    TcpStream::connect(node);
                });
            }
        }

        fn run(self: Arc<Self>) -> Result<(), Box<dyn std::error::Error>> {
            loop {
                // a blocking loop to do something
            }

            Ok(())
        }
    }
}

>Solution :

just use &mut self instead of self, since it will not take ownership of self.

mod mutable_reference {
    struct Server;
    impl Server {
        pub fn start(mut self) {
            self.startup();
            self.config();
            self.bootstrap();
            self.init();
            self.run()
        }
        pub fn startup(&mut self) {
            println!("starting up . . .");
        }
        pub fn config(&mut self) {
            println!("reading config . . .");
        }
        pub fn bootstrap(&mut self) {
            println!("boostraping . . .");
        }
        pub fn init(&mut self) {
            println!("initalizing . . .");
        }
        pub fn run(self) {
            println!("running . . .");
            loop { /* do re mi */ }
        }
    }
}

but if you want the function to take ownership, return Self( or Arc<Self> in your case), in the function to chain different function together.

mod take_ownership {
    struct Server;
    impl Server {
        pub fn start(self) {
            self
                .startup()
                .config()
                .bootstrap()
                .init()
                .run()
        }
        pub fn startup(self) -> Self {
            println!("starting up . . .");
            self
        }
        pub fn config(self) -> Self {
            println!("reading config . . .");
            self
        }
        pub fn bootstrap(self) -> Self {
            println!("boostraping . . .");
            self
        }
        pub fn init(self) -> Self {
            println!("initalizing . . .");
            self
        }
        pub fn run(self) {
            println!("running . . .");
            loop { /* do re mi */ }
        }
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading