Reputation: 1507
Im still trying to wrap my head around lifetimes in rust but ive run into a problem. I have a struct that has a map and im tryign to add to that map in a function. It maps a string id to an object:
pub struct AGVController<'a> {
agv_map: HashMap<&'a str, AGV>
}
impl AGVController<'_> {
// Create a new AGV Controller
pub fn new() -> Self {
Self {
agv_map: HashMap::new()
}
}
// Add a new AGV
pub fn add_agv(&mut self, ip_addr: &str) -> Option<&str> {
let new_agv = AGV::new(ip_addr);
match self.agv_map.get(ip_addr) {
Some(_) => {
let result = format!("AGV with ip address {} already exists", ip_addr);
return Some(result.as_str());
},
None => {
self.agv_map.insert(ip_addr, new_agv);
}
}
return Some("");
}
}
in add new I want to create a new instance and add that to the map. Im getting the following error:
lifetime of reference outlives lifetime of borrowed content...
--> src/agv/agv_controller.rs:58:37
|
58 | self.agv_map.insert(ip_addr, new_agv);
| ^^^^^^^
|
note: ...the reference is valid for the lifetime `'_` as defined here...
--> src/agv/agv_controller.rs:16:20
|
16 | impl AGVController<'_> {
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime defined here
--> src/agv/agv_controller.rs:50:40
|
50 | pub fn add_agv(&mut self, ip_addr: &str) -> Option<&str> {
| ^^^^
It seems like it is saying that the reference in the map will outlive the reference of the parameter passed in, which is fair I guess. Can someone explain to me how I add an entry to a map in a function where the key is passed into the function?
Upvotes: 0
Views: 81
Reputation: 168958
This fails because the lifetime of the ip_addr
reference is only valid while the add_agv
method executes, but it needs to be valid for the 'a
in AGVController<'a>
. To fix this, add the 'a
lifetime to your impl
block and use it for this parameter:
impl<'a> AGVController<'a> {
// ...
pub fn add_agv(&mut self, ip_addr: &'a str) -> Option<&str> {
Which brings us to the second problem: you attempt to return a &str
to a String
that is owned by this function (in result
). This is simply impossible; return an Option<String>
instead, making the appropriate changes to your return
statements.
pub fn add_agv(&mut self, ip_addr: &'a str) -> Option<String> {
return Some(result);
return Some("".to_owned());
Alternatively, consider returning a Result
, which is a better way to indicate an operation that can fail:
use std::error::Error;
use std::fmt::{Display, Formatter, Error as FmtError};
#[derive(Debug)]
pub struct DuplicateIPAddress(pub String);
impl Error for DuplicateIPAddress {}
impl Display for DuplicateIPAddress {
fn fmt(&self, w: &mut Formatter) -> Result<(), FmtError> {
write!(w, "Duplicate IP address {}", self.0)
}
}
Now your function can return Result<(), DuplicateIPAddress>
:
pub fn add_agv(&mut self, ip_addr: &'a str) -> Result<(), DuplicateIPAddress> {
return Err(DuplicateIPAddress(ip_addr.to_owned()));
return Ok(());
Upvotes: 3