Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clippy::drop_ref will trigger on &mut T as well as &T #4461

Open
Lokathor opened this issue Aug 28, 2019 · 6 comments
Open

clippy::drop_ref will trigger on &mut T as well as &T #4461

Lokathor opened this issue Aug 28, 2019 · 6 comments
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have

Comments

@Lokathor
Copy link

the drop_ref lint will trigger on &mut T values being dropped, even though dropping them does have an effect.

@luser
Copy link

luser commented Jan 7, 2020

I ran into this today. :-/

@flip1995 flip1995 added the C-bug Category: Clippy is not doing the correct thing label Jan 7, 2020
@mbrubeck
Copy link
Contributor

mbrubeck commented Jan 7, 2020

In one sense, dropping an &mut T does not have an effect: It doesn't do anything at runtime, and removing the drop(mut_ref) call does not change the semantics of any program that compiles.

On the other hand, drop(mut_ref) can be a useful sort of static assertion, since it can prevent you from accidentally adding code in the future that uses the reference after it should no longer be used. In safe Rust the compiler already prevents this, but it could be useful in unsafe code that uses a mixture of raw pointers and references.

@mbrubeck
Copy link
Contributor

mbrubeck commented Jan 7, 2020

I think keeping this lint enabled by default for &mut T is a good idea. The cases I discussed above where drop(reference) is useful as a compile-time assertion are fairly rare, and only appear in unsafe code where it's worth explicitly opting out of the lint and explaining why. In most Rust code, dropping a reference is not likely to do anything useful.

For example, I talked with @luser about the case he mentioned above, and it turned out that the lint was correct: the drop call had no effect and should be removed.

@phansch phansch added the I-false-positive Issue: The lint was triggered on code it shouldn't have label Dec 22, 2020
@jgarvin
Copy link

jgarvin commented May 10, 2021

I ran into this and still think it's useful for reasons beyond just being an assertion, but it's possible I'm misunderstanding something (I am pretty new to rust), but if I am misunderstanding something it might be worth documenting what is wrong with my reasoning for other newbies who come across this.

Rust requires that you only have one mutable reference at a time to a given object. If you are writing unsafe code, it's definitely possible to write code that violates this language rule. To avoid violating the language rule you may choose to explicitly drop a mutable reference early. Imagine:

fn foo(node: *mut Node) {
    let mut node = unsafe { &mut *node };
    // .. do stuff with node
    let p = node.next;
    drop(node);
    let p = unsafe { &mut *p };
    // ... do stuff with p
}

If it is possible for node.next to be a circular pointer back to the same node, the drop prevents undefined behavior. It seems like it would be possible to avoid these false positives by not triggering it for &muts formed by a dereference inside an unsafe block? Is there a reason this drop would somehow be unnecessary?

@mbrubeck
Copy link
Contributor

mbrubeck commented May 10, 2021

Rust requires that you only have one mutable reference at a time to a given object.

Under non-lexical lifetimes, a reference is only "live" until the point where it is last used. You don't need to explicitly drop it after it is no longer used. For a more detailed model, see the stacked borrows paper.

So the drop in this example has no effect, and you can remove it without introducing UB. However, it's a good example of the "static assertion" use case, since it could prevent you from adding code that introduces UB in the future (by incorrectly using the node reference later in the function).

@NicholasGorski
Copy link

Is there a future where this is changed? This issue was re-opened and remains that way, so adding my +1 to it.

I am in the mixed reference and pointer scenario, and the drop complements an unsafe block:

std::mem::drop(our_ref); // Drops the &mut, not the value.

unsafe {
    // SAFETY: This function will perform a foreign write to the value,
    // invalidating all outstanding references. We have dropped our
    // only reference, and will re-fetch the value afterward to continue.
    self.foo();
}

It's nice to see the SAFETY comment explicitly supported in code. The code is already mentally taxing, and adding clippy attributes detracts from the clarity of the relationship.

Would it make sense to issue the warning when the reference is a function parameter, but permit it otherwise? Since parameters are noalias ("protected" in stacked/tree borrow parlance), dropping them can never help static assert why some following code is safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have
Projects
None yet
Development

No branches or pull requests

7 participants