Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call method with &mut self from inside another method of the same type?

Tags:

rust

I've got a Scanner struct that has a scan(&mut self) method implemented. It looks like this.

pub struct Scanner {
    input: String,
    output: Vec<String>,
    state: ScannerState,
}

impl Scanner {
    pub fn scan(&mut self) {
        self.state = ScannerState::CharMode;
        for character in self.input.chars() {
            match character {
                i @ '0'...'9'   => self.output.push(format!("Integer digit: {}", i)),
                '+'             => self.output.push("Addition operator: +".to_string()),
                '-'             => self.output.push("Subtraction operator: -".to_string()),
                '*'             => self.output.push("Multiplication operator: *".to_string()),
                '/'             => self.output.push("Division operator: /".to_string()),
                '%'             => self.output.push("Modulus operator: %".to_string()),
                '^'             => self.output.push("Exponent operator: ^".to_string()),
                '='             => self.output.push("Assignment operator: =".to_string()),
                ';'             => self.output.push("Statement terminator: ;".to_string()),
                c @ 'a'...'z'| c @ 'A'...'Z'
                                => self.output.push(format!("Variable name: {}", c)),
                ' '             => self.output.push("Space, ignoring.".to_string()),
                z @ _           => self.output.push(format!("Unrecognized token: {}", z))
            }
        }
    }        
}

However, as you might suspect, the state of the Scanner will change, and it will need to treat tokens differently in different states. Therefore, it would be useful to call a private method on the Scanner from within the scan() method itself, akin to this:

pub fn scan(&mut self) {
    self.state = ScannerState::CharMode;

    while self.state == ScannerState::CharMode {
        for character in self.input.chars() {
            self.char_match(character);
        }
        self.state = ScannerState::Done;
    }
}

fn char_match(&mut self, c: char) {
    match c {
        '+'             => self.output.push("Addition operator: +".to_string()),
        '-'             => self.output.push("Subtraction operator: -".to_string()),
        '*'             => self.output.push("Multiplication operator: *".to_string()),
        '/'             => self.output.push("Division operator: /".to_string()),
        '%'             => self.output.push("Modulus operator: %".to_string()),
        '^'             => self.output.push("Exponent operator: ^".to_string()),
        '='             => self.output.push("Assignment operator: =".to_string()),
        ';'             => self.output.push("Statement terminator: ;".to_string()),
        ' '             => self.output.push("Space, ignoring.".to_string()),
        'q'             => self.state = ScannerState::QuitMode,
        i @ '0'...'9'   => self.output.push(format!("Integer digit: {}", i)),
        c @ 'a'...'z'   => self.output.push(format!("Variable name: {}", c)),
        z @ _           => self.output.push(format!("Unrecognized token: {}", z))
    }
}

But wait! We can't do that, Rust tells us:

src/scanner.rs:34:17: 34:21 error: cannot borrow `*self` as mutable because `self.input` is also borrowed as immutable
src/scanner.rs:34                 self.char_match(character);
                                  ^~~~

Yet our char_match() method NEEDS to have a mutable reference to self, since it pushes, and push()ing on a Vec requires mutability. My question then, is given the preceding knowledge, what is the ideal way to remedy this situation?

Will I simply have to write scan() as a long method?

like image 440
bretmattingly Avatar asked Sep 04 '15 18:09

bretmattingly


People also ask

Can we call a method with it?

Yes, you can call methods using it. But, you should call them only from instance methods (non-static).

How do you call a method in main method?

Call a Method Inside main , call the myMethod() method: public class Main { static void myMethod() { System.out.println("I just got executed!"); } public static void main(String[] args) { myMethod(); } } // Outputs "I just got executed!"

How do you call a public method in Java?

To call a method in Java, write the method name followed by a set of parentheses (), followed by a semicolon ( ; ).

How do you call an int method in Java?

You need to store each float in its own variable, maybe firstNumber and secondNumber , then call your method: displayResult(firstNumber, secondNumber) . public static int readNumber(){ int num1, num2; Scanner sc = new Scanner (System.in); System. out. println("Enter first number : "); num1 = sc.


2 Answers

char_match does not need mutable access to self. Mutable access to self.output and self.state will be enough. If you don't give it access to self.input the compiler will no longer complain.

like image 50
FlyingFoX Avatar answered Oct 06 '22 06:10

FlyingFoX


My suggestion would be to make char_match stateless:

pub fn scan(&mut self) {
    self.state = ScannerState::CharMode;

    while self.state == ScannerState::CharMode {
        for character in self.input.chars() {
            match char_match(character) {
                Some(string) => self.output.push(string),
                None => self.state = ScannerState::QuitMode
            }
        }
        self.state = ScannerState::Done;
    }
}

fn char_match(c: char) -> Option<String> {
    Some(match c {
        '+' => "Addition operator: +".into(),
        '-' => "Subtraction operator: -".into(),
        '*' => "Multiplication operator: *".into(),
        '/' => "Division operator: /".into(),
        '%' => "Modulus operator: %".into(),
        '^' => "Exponent operator: ^".into(),
        '=' => "Assignment operator: =".into(),
        ';' => "Statement terminator: ;".into(),
        ' ' => "Space, ignoring.".into(),

        'q' => return None,

        i @ '0'...'9' => format!("Integer digit: {}", i),
        c @ 'a'...'z' => format!("Variable name: {}", c),
        z @ _         => format!("Unrecognized token: {}", z)
    })
}

This avoids borrowing altogether, and is also more composable.

like image 33
Veedrac Avatar answered Oct 06 '22 08:10

Veedrac