Reputation: 1124
I'm newish to Rust and I'm trying to figure out how to fix the Trader.gateway_client
method in the snippet below. It is caused by a double borrow of a mutable. What would be the correct way to fix the code (without cloning the self.gateway
string before).
use std::collections::{HashMap};
pub struct GatewayClient {
gateway: String,
strategy: String,
}
pub struct Trader {
gateway_clients: HashMap<String, GatewayClient>,
strategy: String,
}
impl GatewayClient {
pub fn new(gateway: &str, strategy: &str) -> Self {
GatewayClient {
gateway: String::from(gateway),
strategy: String::from(strategy),
}
}
}
impl Trader {
pub fn new(strategy: &str) -> Self {
Trader {
gateway_clients: HashMap::default(),
strategy: String::from(strategy),
}
}
pub fn gateway_client(&mut self, gateway: &str) -> &mut GatewayClient {
self.gateway_clients.entry(String::from(gateway)).or_insert_with(|| {
GatewayClient::new(gateway, &self.strategy)
})
}
}
The error thrown by the compiler is
63 | pub fn gateway_client(&mut self, gateway: &str) -> &mut GatewayClient {
| - let's call the lifetime of this reference `'1`
64 | self.gateway_clients.entry(String::from(gateway)).or_insert_with(|| {
| -------------------- ^^ immutable borrow occurs here
| |
| _________mutable borrow occurs here
| |
65 | | GatewayClient::new(gateway, &self.strategy)
| | ------------- second borrow occurs due to use of `self` in closure
66 | | })
| |__________- returning this value requires that `self.gateway_clients` is borrowed for `'1`
Upvotes: 0
Views: 437
Reputation: 5385
A way to get around the borrowing conflict would be to eliminate the second explicit borrow within the callback by extracting what you need from self
outside of it.
pub fn gateway_client(&mut self, gateway: &str) -> &mut GatewayClient {
let strategy = &self.strategy;
self.gateway_clients
.entry(gateway.into())
.or_insert_with(|| GatewayClient::new(gateway, strategy))
}
This looks like it may be a second borrow against self
within the callback, but the compiler accepts this.
.or_insert_with()
vs. .or_insert()
A fix was suggested using .or_insert()
, but your choice to use .or_insert_with()
is correct.
The advantage of using .or_insert_with()
over .or_insert()
in this case is the callback only gets executed if the entry doesn't exist. If using .or_insert()
, GatewayClient::new()
would be invoked whether or not the entry existed every time.
Upvotes: 2
Reputation: 7927
You just didn't need to use .or_insert_with
which allows to execute custom code via a closure. You just wanted to insert a value, and there's a method .or_insert
, that accepts a value.
pub fn gateway_client(&mut self, gateway: &str) -> &mut GatewayClient {
self.gateway_clients
.entry(gateway.to_string())
.or_insert(GatewayClient::new(gateway, &self.strategy))
}
Also if you'll ever want to get rid of a million of creations of new strings with String::from(...)
, you can look towards std::borrow::Cow
type
Upvotes: 1