Search code examples
rustimmutabilityrust-obsolete

Rust "error: moving out of immutable field"


I've created the following Rust struct:

struct HTTPRequestHeader {
    name: ~str,
    data: ~str,
    next: Option<~HTTPRequestHeader>
}

And the following code to print it:

fn print_headers(hdr: &HTTPRequestHeader) {
    println(fmt!("%s: %s", (*hdr).name, (*hdr).data));
    match (*hdr).next {
        Some(next_hdr) => { print_headers(next_hdr); }
        None => { }
    }
}

Trying to compile this code, I get the following error:

> rustc http_parse.rs
http_parse.rs:37:7: 37:18 error: moving out of immutable field
http_parse.rs:37    match (*hdr).next {
                          ^~~~~~~~~~~
error: aborting due to previous error

What does this mean? The line which contains (*hdr).name, (*hdr).data) compiles without errors. The match statement should not be attempting to mutate hdr.next in any way, so I don't see how immutability comes into it. I attempted to write a modified version which doesn't take pointers:

fn print_headers(hdr: HTTPRequestHeader) {
    println(fmt!("%s: %s", hdr.name, hdr.data));
    match hdr.next {
        Some(next_hdr) => { print_headers(*next_hdr); }
        None => { }
    }
}

This one gave me:

> rustc http_parse.rs
http_parse.rs:37:7: 37:15 error: moving out of immutable field
http_parse.rs:37    match hdr.next {
                          ^~~~~~~~
http_parse.rs:38:36: 38:45 error: moving out of dereference of immutable ~ pointer
http_parse.rs:38        Some(next_hdr) => { print_headers(*next_hdr); }
                                                          ^~~~~~~~~
error: aborting due to 2 previous errors

Please help me understand what's going on here! :)


Solution

  • While the error does tell you where you're trying to move the value from it doesn't indicate where you're trying to move it to which might help make it a bit clearer why it doesn't work.

    The problem is that you have a unique pointer (Option<~HTTPRequestHeader>) which means you either need to take a reference to it or make a copy. Since they can only have a single owner they are moved by default. Which is what is happening in the Some(next_hdr) branch of your match.

    So what you probably want is something like this:

    fn print_headers(hdr: &HTTPRequestHeader) {
        println(fmt!("%s: %s", hdr.name, hdr.data));
        match hdr.next {
            Some(ref next_hdr) => print_headers(*next_hdr),
            None => {}
        }
    }
    

    Also, a side note: if you want to access a field of some struct through a pointer you don't need to explicitly dereference it (i.e. just hdr.name works as well as (*hdr).name).