1

I am writing a small program to identify the first recurring character in a string:

use std::io;

fn main() {
    let mut main_string = String::new();

    println!("Please enter the string : ");
    io::stdin()
        .read_line(&mut main_string)
        .expect("Failed to read the input value");

    main_string = main_string.trim().to_string();
    println!("The trimmed string is : {}", main_string);

    let repeating_character = recurring_string_parser(main_string);

    println!(
        "The character which is first repeating is {}",
        repeating_character
    );
}

fn recurring_string_parser(main_string: String) -> char {
    let mut char_array = Vec::new();
    for each_char in main_string.chars() {
        let mut some_val = char_array.iter().find(|&&c| c == each_char);

        match some_val {
            Some(ch) => return each_char,
            _ => println!("do nothing"),
        }
        char_array.push(each_char);
        println!(" The charcater is {:?}", some_val);
    }

    return 'a';
}

Error:

error[E0502]: cannot borrow `char_array` as mutable because it is also borrowed as immutable
  --> src/main.rs:31:9
   |
25 |         let mut some_val = char_array.iter().find(|&&c| c == each_char);
   |                            ---------- immutable borrow occurs here
...
31 |         char_array.push(each_char);
   |         ^^^^^^^^^^ mutable borrow occurs here
32 |         println!(" The charcater is {:?}", some_val);
33 |     }
   |     - immutable borrow ends here

What is it that I am doing wrong? It would be of great help if someone could explain it as well because I am finding it difficult to grasp the concept of mutable borrowing.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
damnsun
  • 13
  • 1
  • 2
  • 1
    `char_array` isn't what its name suggests. (i.e., it's not a `Vec`) – trent Nov 02 '17 at 14:43
  • That was a mistake I had overlooked but now it is corrected, but that was not causing the problem. – damnsun Nov 02 '17 at 18:06
  • Actually, I contest that it *was* causing the problem, since [adding `.cloned()` (or, equivalently, `.map(|r| *r)`) fixes it.](https://play.rust-lang.org/?gist=c7654bb8f0ff9572b15e4a72d93202f5&version=stable) (As Shepmaster's answer also observes.) – trent Nov 02 '17 at 18:54

1 Answers1

2

The return type of char_array.iter().find(|&&c| c == each_char) is an Option<&char>; a reference pointing at the original item in char_array. You then attempt to modify char_array which could invalidate any references. After that, you try to access the now-potentially-invalid value by printing it.

In a language like C or C++, this code would be allowed but would occasionally crash, corrupt data, allow for arbitrary code execution, or eat your laundry.

The laziest solution is to reorder the println and add an extra set of braces around where some_val should live. (In the future we will have Non-Lexical Lifetimes and the extra braces won't be needed):

{
    let mut some_val = char_array.iter().find(|&&c| c == each_char);

    match some_val {
        Some(ch) => return each_char,
        _ => println!("do nothing"),
    }
    println!(" The character is {:?}", some_val);
}

char_array.push(each_char);

You could also clone the value, breaking the relation between some_val and char_array:

let mut some_val = char_array.iter().find(|&&c| c == each_char).cloned();

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366