0

I only just started learning rust and don't quite understand the referencing system that rust uses yet. In the following function, I am trying to write code for FizzBuzz that assigns result a &str depending on the lowest common multiple of a u32 in a range.

fn fizzbuzz(last_num: u32) {
    for i in 1..last_num+1 {
        let result = if i % 15 == 0 {
            "FizzBuzz"
        } else if i % 3 == 0 {
            "Fizz"
        } else if i % 5 == 0 {
            "Buzz"
        } else {
            &i.to_string()[..]
        };
        println!("{}", result);
    }
}

in my else clause, I get the following error:

11 |           } else if i % 5 == 0 {
   |  ________________-
12 | |             "Buzz"
13 | |         } else {
14 | |             &i.to_string()[..]
   | |              ^^^^^^^^^^^^^ creates a temporary which is freed while still in use       
15 | |         };
   | |         -
   | |         |
   | |_________temporary value is freed at the end of this statement
   |           borrow later used here

From what I understand so far about rust, this shouldn't be an issue because the &i reference is being used before the end of the scope where i is freed from memory.

What exactly am I doing wrong, and what is the fix?

neRienn
  • 41
  • 3
  • Essentially a duplicate of [Return local String as a slice (&str)](https://stackoverflow.com/questions/29428227/return-local-string-as-a-slice-str). You need to return an owned string. – Herohtar Aug 07 '20 at 02:52

1 Answers1

0

The problem is not &i reference, but created something new as a temporary variable which is i.to_string() (String type) then it's being used to create str (primitive type) referring the String type which later is being destroyed because it's temporary. Meaning you're calling something inexistent. and String and str are different, you can see the explanation here.

the easiest way to fix this is to use String instead:

fn fizzbuzz(last_num: u32) {
    for i in 1..last_num+1 {
        let result: String = if i % 15 == 0 {
            String::from("FizzBuzz")
        } else if i % 3 == 0 {
            String::from("Fizz")
        } else if i % 5 == 0 {
            String::from("Buzz")
        } else {
            i.to_string()
        };
        println!("{}", result);
    }
}

but if you want to stay using the str type, you can do it like below:

fn fizzbuzz(last_num: u32) {
    for i in 1..last_num+1 {
        let wont_be_destroyed: String = i.to_string(); 
        let mut result = &wont_be_destroyed[..];
        if i % 15 == 0 {
            result = &"FizzBuzz"
        } else if i % 3 == 0 {
            result = &"Fizz"
        } else if i % 5 == 0 {
            result = &"Buzz"
        }
        println!("{}", result);
    }
}
Shodiq
  • 51
  • 3
  • Note however that your first solution allocates, copies and drops the strings at each iteration, and that your second solution wastes resources converting `i` to string even when printing a predefined string. It would be better to either print directly inside the `if`, without going through `result`, or to use a [`Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html). – Jmb Aug 07 '20 at 06:25
  • 3
    @Jmb: Or even better, use the uninitialized variable trick: `let si;` before the `if` and then `si = i.to_string(); &si` inside ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2cf3db4b0552128aef78e54e9707afaa)) – rodrigo Aug 07 '20 at 07:30