discodowney
discodowney

Reputation: 1507

Inserting into a map in a function

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

Answers (1)

cdhowie
cdhowie

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());

(Playground)


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(());

(Playground)

Upvotes: 3

Related Questions