Problems with new block scoping rules


#1

Hi Folks,

I’m having some problems understanding the new block scoping rules introduced in #21972 (not that I understood the old ones very well, but that’s another story…)

Here’s a cut down version of the code which has started failing:

fn main() {
    let mut vm;
    let mut options:Options = Default::default();
    let (server_tx, vm_rx) = sync_channel(16);
    vm = Vm::new(&options, vm_rx);
    vm.start().unwrap();
}

The error message I get is:

src/main.rs:95:5: 95:7 error: `vm` does not live long enough
src/main.rs:95     vm.start().unwrap();
                   ^~
src/main.rs:57:11: 145:2 note: reference must be valid for the block at 57:10...
src/main.rs:57 fn main() {
src/main.rs:58     let mut vm;
src/main.rs:59     /*
src/main.rs:60     let args: Args = Docopt::new(USAGE).and_then(
src/main.rs:61                          |d| d.decode()
src/main.rs:62                      ).unwrap_or_else(
               ...
src/main.rs:58:15: 145:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 58:14
src/main.rs:58     let mut vm;
src/main.rs:59     /*
src/main.rs:60     let args: Args = Docopt::new(USAGE).and_then(
src/main.rs:61                          |d| d.decode()
src/main.rs:62                      ).unwrap_or_else(
src/main.rs:63                          |e| e.exit()
               ...

I don’t understand how I could have a reference which was valid for the very start of my main function - this doesn’t seem possible to me.

Any help on this would be much appreciated.

Cheers,
Joe


#2

Does swapping the order of declaration of vm and options work? If you’re storing those options by reference, then the options will be dropped before the VM is, causing a potential use-after-free.

This is why I would advise against declaring a variable before initializing it. If you just had:

fn main() {
    let mut options:Options = Default::default();
    let (server_tx, vm_rx) = sync_channel(16);
    let mut vm = Vm::new(&options, vm_rx);
    vm.start().unwrap();
}

This kind of error couldn’t happen.

That said with this tiny fragment of code it’s not clear to me if that’s what’s going wrong.


#3

That was what I initially had - moving vm to the top of the function was my attempt to fix the problem based on the guidance in the PR. When I move the declaration of vm to where I call VM::new I get the same error, just in a different location:

src/main.rs:95:5: 95:7 error: `vm` does not live long enough
src/main.rs:95     vm.start().unwrap();
                   ^~
src/main.rs:92:46: 147:2 note: reference must be valid for the block suffix following statement 1 at 92:45...
src/main.rs:92     let (server_tx, vm_rx) = sync_channel(16);
src/main.rs:93 
src/main.rs:94     let mut vm = Vm::new(&options, vm_rx);
src/main.rs:95     vm.start().unwrap();
src/main.rs:96     /*
src/main.rs:97 
               ...
src/main.rs:94:42: 147:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 94:41
src/main.rs:94     let mut vm = Vm::new(&options, vm_rx);
src/main.rs:95     vm.start().unwrap();
src/main.rs:96     /*
src/main.rs:97 
src/main.rs:98     let mut byte_code = ByteCode {
src/main.rs:99         data: [0; 1024],
               ...

Could the error be due to what happens internally in VM, or is it solely a lifetime issue within the main function? I guess I’m a little stumped as to where to look in the rest of the codebase as I’m not completely sure what the error is telling me.


#4

Without the VM code I don’t see any obvious problem, sorry.


#5

cc @pnkfelix is the greatest


#6

Thanks for taking a look folks. I’m going to try to wittle this down to a self-contained test case, as I wouldn’t want to subject anyone to the barely tested, completely undocumented mess that is rest of my code.


#7

Okay, I’ve managed to work out where this is failing. There’s an unsafe destructor within one of the libraries I’m using. My much reduced test case is as follows or as a playpen:

#![feature(unsafe_destructor)]

struct Vm<'a> {
    stream: Option<Stream<'a>>
}

impl<'a> Vm<'a> {
    fn start(&'a mut self) {
    }
}

pub type StreamCallback<'a> = FnMut() + 'a;

struct Stream<'a> {
    callback: &'a mut StreamCallback<'a>
}

#[unsafe_destructor]
impl<'a> Drop for Stream<'a>
{
    fn drop(&mut self) {
    }
}

fn main() {
    let mut vm = Vm {
        stream: None
    };
    vm.start();
}

Is this what is supposed to happen in this case? And is there a workaround?


#8

you should not have the 'a bound on the start functions’ self parameter. If you need an explicit bound, create a new one just for the function.

The error message is not helpful though.

The problem is something along the lines of “start borrows mutably for all of main -> drop can’t borrow mutably either”.


#9

Thanks Oli, that explaination makes a lot of sense. So with the old rules it was fine, because the drop happened after the end of the function, whereas now the drop happens just before the end of the function right? Due to the design at the minute I need vm borrowed for the duration of main, but I think I can probably introduce (yet) another layer of indirection to get around it. Will have a play and see how I get on.