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
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(())
}
}
}
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 */ }
}
}
}