lgg
lgg

Reputation: 83

Peeking at stdin using match

I'm trying to port a translator/parser example from an old compiler textbook from C into Rust.

I have the following code:

use std::io::Read;

fn lexan() {
    let mut input = std::io::stdin().bytes().peekable();
    loop {
        match input.peek() {
            Some(&ch) => {
                match ch {
                    _ => println!("{:?}", input.next()),
                }
            }
            None => break,
        }
    }
}

At this point I'm not actively trying to parse the input, just get my head around how match works. The aim is to add parse branches to the inner match. Unfortunately this fails to compile because I appear to fail in understanding the semantics of match:

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:7:18
  |
7 |             Some(&ch) => {
  |                  ^--
  |                  ||
  |                  |hint: to prevent move, use `ref ch` or `ref mut ch`
  |                  cannot move out of borrowed content

From what I understand, this error is because I don't own the return value of the match. The thing is, I don't believe that I'm using the return value of either match. I thought perhaps input.next() may have been the issue, but the same error occurs with or without this part (or indeed, the entire println! call).

What am I missing here? It's been some time since I looked at Rust (and never in a serious level of effort), and most of the search results for things of this nature appear to be out of date.

Upvotes: 1

Views: 649

Answers (1)

Shepmaster
Shepmaster

Reputation: 431789

It's got nothing to do with the return value of match, or even match itself::

use std::io::Read;

fn lexan() {
    let mut input = std::io::stdin().bytes().peekable();
    if let Some(&ch) = input.peek() {}
}

The issue is that you are attempting to bind the result of Peekable::peek while dereferencing it (that's what the & in &ch does). In this case, the return type is an Option<&Result<u8, std::io::Error>> because the Bytes iterator returns errors from the underlying stream. Since this type does not implement Copy, trying to dereference the type requires that you transfer ownership of the value. You cannot do so as you don't own the original value — thus the error message.

The piece that causes the inability to copy is the error type of the Result. Because of that, you can match one level deeper:

match input.peek() {
    Some(&Ok(ch)) => {
        match ch {
            _ => println!("{:?}", input.next()),
        }
    }
    Some(&Err(_)) => panic!(),
    None => break,
}

Be aware that this code is pretty close to being uncompilable though. The result of peek will be invalidated when next is called, so many small changes to this code will trigger the borrow checker to fail the code. I'm actually a bit surprised the above worked on the first go.

If you didn't care about errors at all, you could do

while let Some(&Ok(ch)) = input.peek() {
    match ch {
        _ => println!("{:?}", input.next()),
    }
}

Unfortunately, you can't split the middle, as this would cause the borrow of input to last during the call to next:

while let Some(x) = input.peek() {
    match *x {
        Ok(ch) => {
            match ch {
                _ => println!("{:?}", input.next()),
            }
        }
        Err(_) => {}
    }

    // Could still use `x` here, compiler doesn't currently see that we don't
}

Upvotes: 1

Related Questions