skip to Main Content

I am trying to do simple telegram bot. I have to response some answer for provided questions.

Problem is that I can’t use borrowed parts of question (string) to pass through it to db save function.

I’ve cut my code as mush as possible:

pub enum Answer {
    DbCommand(Box<dyn Fn()>),
}

pub fn process(question: &str) -> Answer {
    let parts: Vec<&str> = question
        .split(" ")
        .collect();

    let channel = parts.get(1).unwrap();

    Answer::DbCommand(Box::new(|| {
        save_to_db(channel)
    }))
}

pub fn save_to_db(chan: &str) {
    // Saving to db
}

Playground

Output is:

error[E0621]: explicit lifetime required in the type of `question`
  --> src/lib.rs:12:23
   |
5  |   pub fn process(question: &str) -> Answer {
   |                            ---- help: add explicit lifetime `'static` to the type of `question`: `&'static str`
...
12 |       Answer::DbCommand(Box::new(|| {
   |  _______________________^
13 | |         save_to_db(channel)
14 | |     }))
   | |______^ lifetime `'static` required

If I add some function lifetime, then I get error E0495. There’s not much information on it

2

Answers


  1. Chosen as BEST ANSWER

    My final code looks like this:

    pub enum Answer<'a> {
        Text(String),
        DbCommand(Box<dyn Fn() -> Result<String, Error> + 'a>),
    }
    
    pub fn process(question: &str) -> Answer {
        let mut parts = question
            .split(" ")
            .map(str::trim)
            .filter(|s| !s.is_empty());
    
        let command = parts.next();
    
        match command {
            //...
            Some("/subscribe") => {
                match parts.next() {
                    Some(channel) => {
                        Answer::DbCommand(Box::new(move || {
                            db::subscribe_to_channel(&channel)
                            //...
                        }))
                    },
                    None => Answer::Text("Provide channel name".into()),
                }
            },
            _ => Answer::Text("Invalid command.".into()),
        }
    }
    

  2. split does not allocate anything, it only iterates over the initial string, keeping a reference to it. You need to own the string and move it into the closure:

    pub enum Answer {
        DbCommand(Box<dyn Fn()>),
    }
    
    pub fn process(question: &str) -> Answer {
        let channel = question.split(" ").nth(1).unwrap().to_owned();
    
        Answer::DbCommand(Box::new(move || save_to_db(&channel)))
    }
    
    pub fn save_to_db(chan: &str) {
        // Saving to db
    }
    

    By the way, you do not need to collect anything in this case.

    If you really don’t want to allocate a string, you can make your struct generic over a lifetime, but I think that this would add an unneeded complexity.:

    pub enum Answer<'a> {
        DbCommand(Box<dyn Fn() + 'a>),
    }
    
    pub fn process(question: &str) -> Answer {
        let channel = question.split(" ").nth(1).unwrap();
    
        Answer::DbCommand(Box::new(move || save_to_db(channel)))
    }
    
    pub fn save_to_db(chan: &str) {
        // Saving to db
    }
    

    That’s because a trait object has an implicit 'static lifetime by default.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search