pellucid
pellucid

Reputation: 293

How to resolve Rust's "use of moved value" in method design?

I have a System, which will connect multiply API downstreams. I would like to use

let mut system = System::new()

to intake config and do the validation, then use system.init() to init all connections for the downstreams. After it connects all downstreams, I would like to make multiple methods to do CRUD to the downstreams.

Here's the playground

struct Conn {
    connection: String,
}
impl Conn {
    fn fetch(&mut self) -> &str {
        self.connection.push_str("data");
        &self.connection
    }
}

struct System {
    downstream: Option<Conn>,
}

impl System {
    fn new() -> Self {
        System { downstream: None }
    }

    fn init(&mut self) {
        self.downstream = Some(Conn {
            connection: String::from("db connection"),
        })
    }

    fn method_a(self) -> String {
        let mut conn = self.downstream.unwrap();
        String::from(conn.fetch())
    }
    fn method_b(self) -> String {
        let mut conn = self.downstream.unwrap();
        String::from(conn.fetch())
    }
}

fn main() {
    let mut system = System::new();
    system.init();

    println!("{}", system.method_a());
    println!("{}", system.method_b());
}

However, because system.method_a() moved the ownership at unwrap(), so when I call the second method system.method_b() after the first one. Obviously, I will get the error "use of moved value".

I understand how and why the error happens. But I don't know what's the idiomatic way to deal with this situation.

One workaround way is use Conn instead of Option<Conn>, but if don't use Option, then I have to init all downstream connections in System.new() and all downstreams can't have the value of "None".

So my question is how I design a module to fix my purpose?

Thanks in advance.

Upvotes: 2

Views: 849

Answers (1)

Mihir Luthra
Mihir Luthra

Reputation: 6779

In the example you provided, you could change method_a and method_b to take a mutable reference instead. conn itself just calls fetch() which uses mutable ref.

fn method_a(&mut self) -> String {
    self.downstream
        .as_mut()
        .map(|conn| String::from(conn.fetch()))
        .unwrap()
}

fn method_b(&mut self) -> String {
    self.downstream
        .as_mut()
        .map(|conn| String::from(conn.fetch()))
        .unwrap()
}

Playground

For undertanding purposes, you could also write the above method as:

fn method_a(&mut self) -> String {
    match self.downstream {
        Some(ref mut conn) => String::from(conn.fetch()),
        None => panic!(),
    }
}

Playground

And if the above code is just an example snippet and you need ownership of conn for some reason, you can do it by take() and if that also doesn't work then you will have to make your struct implement Clone trait so you can clone it instead of moving.

Upvotes: 8

Related Questions