How do I make format! return a &str from a conditional expression?

后端 未结 3 442
悲&欢浪女
悲&欢浪女 2020-12-07 02:21

I happened upon this problem where format! creates a temporary value in a pattern that is not anchored to anything, as far as I understand it.

l         


        
相关标签:
3条回答
  • 2020-12-07 03:00

    format! can't return &str because it will always allocate String. What is possible to do is to return a &str from a String, which is what you did in your code.

    As the compiler hinted, the created String is immediately dropped after its creation because it went out of the current scope and one way around could be an external variable that is not bounded to the match scope. E.g.:

    use std::fmt::Write;
    
    fn main() {
        let mut buffer = String::with_capacity(20);
        buffer.push_str("It's a ");
    
        let x = 10;
        let category = match x {
            0...9 => "Between 0 and 9",
            number @ 10 => {
                write!(&mut buffer, "{}", number).unwrap();
                buffer.as_str()
            }
            _ if x < 0 => "Negative",
            _ => "Something else",
        };
    
        println!("{}", category);
    }
    

    If you want an [no_std] environment or don't want to do any dynamic allocation, you can take a look at this limited code snippet:

    use core::str;
    
    fn each_digit<F>(mut number: u32, mut f: F)
    where
        F: FnMut(u8),
    {
        while number > 0 {
            f((number % 10) as u8);
            number /= 10;
        }
    }
    
    fn main() {
        const BUFFER_LEN: usize = 20;
        let mut buffer = [0u8; BUFFER_LEN];
    
        let x = 12344329;
        let category = match x {
            0...9 => "Between 0 and 9",
            number @ 123443219 => {
                let mut idx = BUFFER_LEN;
                each_digit(number, |digit| {
                    let ascii = digit + 48;
                    idx -= 1;
                    buffer[idx] = ascii;
                });
                str::from_utf8(&buffer[idx..BUFFER_LEN]).unwrap()
            },
            _ => "Something else",
        };
    
        assert_eq!("123443219", category);
    }
    
    0 讨论(0)
  • 2020-12-07 03:03

    In my case How to overcome "temporary value dropped while borrowed" when converting an i32 to &str
    I could solve it by moving the call inside the branches

    pub fn uidl(&mut self, message_number: Option<i32>) -> POP3Result {
        let command = match message_number {
            Some(_) => POP3Command::UidlOne,
            None => POP3Command::UidlAll,
        };
    
        match message_number {
            Some(i) => {
                // Here the value is not dropped because it is not leaving the scope
                self.execute_command(command, Some(arg.to_string().as_str()))
            }
            // Here I had to duplicate the call
            None => self.execute_command(command, None),
        }
    }
    

    Kind of what is suggested in the error message https://doc.rust-lang.org/error-index.html#E0597

    0 讨论(0)
  • 2020-12-07 03:06

    This is 90% a duplicate of Return local String as a slice (&str), see that for multiple other solutions.

    There's one extra possibility since this is all in one function: You can declare a variable for the String and only set it when you need to allocate. The compiler (obliquely) suggests this:

    consider using a let binding to create a longer lived value

    fn main() {
        let x = 42;
        let tmp;
    
        let category = match x {
            0...9 => "Between 0 and 9",
            number @ 10 => {
                tmp = format!("It's a {}!", number);
                &tmp
            }
            _ if x < 0 => "Negative",
            _ => "Something else",
        };
    
        println!("{}", category);
    }
    

    This is mostly the same as using a Cow, just handled by the compiler instead of a specific type.

    0 讨论(0)
提交回复
热议问题