This code is wrong, but it worked - once. Wut?

So, I wrote a quick main.rs just to test what happens when you call pop() on a Vec contained in a struct. As you will quickly see, it shouldn't have worked ...


#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]

#[derive(Debug)]
struct Stuff {
    things: Vec<usize>,
}

fn main (){
    let stuff = Stuff { things: vec![8, 6, 7, 5, 3, 0, 9]};
    println!("{:?}", stuff);
    println!("==========================================");
    stuff.data.pop();
    stuff.data.pop();
    println!("{:?}", stuff);
}

This is the entirety of the code in the crate, and there are no dependencies. I'm running Rust 1.66.1. Anyway, I ran cargo run - and this wrong code compiled and ran - the first time I tried. But the results were wrong, so I took a closer look, and noticed I was referencing a nonexistent struct member - data (just because I typically use that name for the main internal data structure inside a struct). So I changed data to things, and then got the message that stuff needed to be declared mut. All pretty much no-brainer stuff. And when I made those changes, the program compiled and produced the expected results.

But why did it compile the first time??

Just to be sure, I changed the code back to the original, wrong version, and then the compiler rejected it. But it worked once. I swear I am not imagining this. I can still see the output in my terminal.

So, this is really weird. Maybe a bug in the compiler? How would I determine if that is the case (given that I'm fairly new to Rust and know nothing at all about the internals)?

1 Like

You're probably misremembering what happened.

Thanks, but no. I wrote this code less than 10 minutes ago. And, you don't know me, so you can believe it or not, but I have a ridiculously good memory.

EDIT: Okay, I can't be sure that the last version is character-for-character identical to the first - I'm not checking this into Git or anything (LOL) - but I definitely started out referencing stuff.data.

If you did write some code previously that did include .data fields, and you keep incrementally re-compiling without any cargo clean in between, it’s not entirely out of the question that you might have encountered a bug in rustc’s implementation of incremental compilation. If this really was the case, it would of course be very interesting if the bug is reproducible, but that might require you being able to reproduce exactly sufficiently many steps of modification and compilation you did before the surprisingly successfully compiling code.

Possibly more likely than a compiler bug is human error though. If you use e.g. an editor and a separate terminal for compilation, you might have simply forgot to save before compilation, resulting in misremembering code to have been compiling successfully, when it really hadn’t.

4 Likes

Yes, this is the case. I have a "scratch" crate that I use to write small experimental programs when I'm unsure of how something works and want to try it out without messing up the main project I'm working on. I've had this crate going for probably a week and have written a lot of different, unrelated code in it, and I'm pretty sure I've had structs containing data fields previously.

Hmm, I don't think I can do that. I'm in an intensive learning phase with Rust, and working on a smallish but non-trivial real-world project, and ... no, really don't remember all I have done in this crate.

I'm still sure that's not what happened. After being away from my computer for 1 hour or so, I sat down, deleted all the dependencies from Cargo.toml, deleted everything except the allow macros from main.rs, and wrote the above code, all in the space of 2-3 minutes. And the results I saw from the first version, although wrong, couldn't have come from any code I wrote before.

But I guess it doesn't really matter, if I can't reproduce the weird behavior.

EDIT: And I guess I'll mark this solved ... further discussion might be interesting, but I can't see any way to definitively account for what happened.

1 Like