Do _ variables shorten the scope/lifetime?


#1

I have some code that requires creating a structure that I don’t directly use in the rust code. However the new structure is managing something in C (in my case a GL context) which does need to be created (and managed). I am using the scope of the structure in rust to manage the lifetime of the “thing” in C.

Because I don’t directly use the variable in my rust code the compiler correctly warns me about an unused variable. However the structure does correctly live to the end of the block/scope.

If I then replace the variable name with _ the structure is immediately dropped and does not live to the end of the block/scope - is this correct functionality?

I can understand that the compiler can deduce that the _ variable isn’t referenced by anything. However does this mean that things technically aren’t scoped around blocks but scope can stop anywhere?

If it is how do I get around the warning?

  1. #[allow(unused_variables)] - this seems hacky
  2. use my variable for some pointless thing - this also seems hacky

Here is some example code to show the issue

struct MyType {}

impl MyType {
    fn new() -> MyType {
        println!("MyType new");

        MyType {}
    }
}

impl Drop for MyType {
    fn drop(&mut self) {
        println!("MyType drop");
    }
}

fn main() {
    let unused = MyType::new();

    println!("And I'm here");
}

With the “unused” variable name this code compiles with a warning and outputs the following expected lines

MyType new
And I'm here
MyType drop

If I replace “unused” with _ the code compiles without any warnings (as expected) but the output isn’t expected

MyType new
MyType drop
And I'm here

Any help to clear up this confusion is greatly appreciated


#2

Yes, that is correct. I read let _ = ... as “explicitly not stored at all”. You can prefix a variable with _ though, like let _unused = ... to store it and tell the compiler that you’re fine with not using it.

This is for example quite common (at least for me) when you’re implementing a trait method and aren’t interested in all parameters, but still want to have them named for documentation purposes.


#3

_ is not actually a variable, it is pattern syntax for “don’t bind this to anything”. So the let _ = x; is equivalent to just x; (except that it doesn’t trigger “unused result” warnings like for Result types).

The easiest way to fix your problem is to name the variable _something, which is a variable binding pattern but also one that the compiler recognizes as “unused is allowed”.


#4

Thank you for those excellent answers.

I’d assumed that _ worked more like an anonymous variable, ie it existed (as a variable and a thing) but you just couldn’t refer to it in any way.

I will use the _something approach as that works perfectly.


#5

Depending on the situation, you also might want to consider refactoring your API to require passing a reference to the context object to functions that depend on the state it implicitly manages (especially moving free functions to be methods on the context object). That way, the lifetime checks serve as a proxy for guaranteeing that that state is active at the point you use those functions, and of course the object would then no longer be unused.


#6

I did consider passing a reference to the context object in. However I’ve split my code into a higher level crate and a lower level binding crate. The context object lives in the higher level code (and uses the lower level binding code to create a gl context etc). However the lower level binding code actually does the work, of which some of it requires a gl context to exist. I didn’t want to move the context object code into the binding crate, and didn’t want the binding crate to suddenly require more higher level concepts.

I also considered adding a method on the context object that just in turn called the binding code. However I’m not a big fan of methods that just call another method and don’t really add any “value”. It brings back java memories of massive stacks where 90% of the code just calls the next method.

In this particular case I’m actually using wglGetProcAddress to retrieve function pointers inside the OpenGL library (as a helper function in the binding code). This function needs a gl context to exist, but doesn’t directly use the context (its not passed in on the API). Its all very iffy and unfortunately how the layer between Windows (or any OS) and OpenGL works.

Its just one of those things when trying to massage a very old C API design into a more modern language.


#7

Is it necessary to store _unused when it’s unused for the rest of the program? Compiler can (and probably does) not store it when it’s not used?


#8

@fireion it might be useful for implementing certain kings of scope guards. For example, in Cargo we have a function to measure execution time of a code block, and it works by creating a guard object which, in Drop, records the elapsed time. We need this guard to live throughout the scope, but otherwise we don’t use it in any way, so _p pattern is used: