# \[Idea\] Attributes to warn if value is dropped without being acted upon

**URL:** https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485
**Category:** language design
**Created:** [June 28, 2019, 9:02pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485 "2019-06-28T21:02:37Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 28, 2019, 9:02pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/1 "2019-06-28T21:02:37Z")

</div>

There has been a long unresolved issue with types that need some operation to be performed on them before the value is dropped. Examples are:

1. Handles for external resources where the finalizing operation needs to be performed explicitly, because it may block or produce a result that should be observed and acted upon. See [RFC PR #2677](https://github.com/rust-lang/rfcs/pull/2677) for some recent discussion.
2. Low-level APIs where an unsafe function must be called on the object before it is dropped. See code in [this proposal](https://github.com/tokio-rs/bytes/issues/269) for an example (I might be doin’ it wrong, though; comments are welcome).

The idea is to add a pair of attributes: one to mark types or function return values similar to `must_use`, that will cause a compiler warning if the value is dropped without being _consumed_ in a way designated by the other attribute:

```rust
#[must_consume = ".close() should be called on the file"]
struct File {
    // ...
}

impl File {
    #[consumes(self)]
    fn close(self) -> io::Result<()> {
        // ...
        Ok(())
    }
}

```

The `consumes` attribute suppresses the `must_consume` lint about that parameter value within the body of the function.

This is trickier for the problem number 2 above, where the value is borrowed (e.g. in a `Drop` impl). In this case, another function attribute can be used to warn if a function with this attribute is not called by the destructor of a containing type, marking the passed member as consumed:

```rust
trait MemHandle {
    // ...
    #[consumes_in_drop(self)]
    unsafe fn release(&mut self);
}

```

Has anyone considered this solution before? Is it worthy of an RFC?

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [June 28, 2019, 11:40pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/3 "2019-06-28T23:40:54Z")

</div>

Sounds like you want some weak form of linear types? @Gankra has previously written some stuff on this: [https://gankro.github.io/blah/linear-rust/](https://gankro.github.io/blah/linear-rust/), which goes into detail as to why a _strict_ version of this is sad. However, I think this suffers a lot of the same problems, insofar that you want the compiler to be able to trace the lifetime of an object in an annotated way (i.e., you sort of want a way for functions to say “I not only accept a `T`, but actually make sure to _consume_ it.”); I don’t see how this proposal addresses that detail in a way that is not, to quote the linked blagpost, deeply unpleasant.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 29, 2019, 6:08am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/4 "2019-06-29T06:08:24Z")

</div>

> [@mcy](#):
>
> you sort of want a way for functions to say “I not only accept a `T` , but actually make sure to _consume_ it.”

Not really; I want a way to say "This function is the correct way to finish usage of `T`, and if you don't, the program may not do what it's supposed to, or leak resources, while remaining safe in the Rust sense". Similarly to how `must_use` indicates return values that are important and _should_ be made use of in some way, so immediately dropping the value is a bad code smell, though not a safety hazard. I don't need it to be bulletproof against more elaborate ways to move and dispose of (or fail to dispose of) a value that @Gankra's blog post talked about.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 29, 2019, 7:43am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/5 "2019-06-29T07:43:32Z")

</div>

Embedding of `#[must_consume]` fields is interesting. A struct with a `#[must_consume]` field would be obliged to have a `#[must_consume]`/`#[consumes(...)]` infrastructure of its own. Dealing with enum values which cannot be statically proven to be, or not to be, of a `#[must_consume]` variant can prove to be a massive pain in practice, but it can also nudge towards linear/affine type designs such as:

```rust
#[must_consume]
struct MayWorkWithFile {
    file: Option<File>,
    stuff: Vec<u8>
}

impl MayWorkWithFile {
    #[consumes(self)]
    fn done(self) -> io::Result<DoneWithFile> {
        self.file.map(|f| f.close()).transpose()?;
        DoneWithFile { stuff: self.stuff }
    }
}

```

---

<div class="post-metadata">

### Author: ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)
#### Post date: [June 29, 2019, 9:05am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/6 "2019-06-29T09:05:59Z")

</div>

My first question would be, why can’t this be solved with a custom Drop (or, after the mentioned RFC, TryDrop) impl that takes care of the required finalization?

The other potentially interesting approach in my mind is to recast this as saying that a type cannot be dropped (through an attribute or marker trait), and then offer a method on the type that takes a `self` argument and destructures it.

(In general, the latter approach seems like it makes the proposes `consumes` attribute unnecessary.)

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 29, 2019, 2:21pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/7 "2019-06-29T14:21:27Z")

</div>

> [@djc](#):
>
> My first question would be, why can’t this be solved with a custom Drop (or, after the mentioned RFC, TryDrop) impl that takes care of the required finalization?

`Drop` cannot really do scary things like blocking because it can be invoked in an unwind. The proposed `Close` or `TryDrop` trait would need to be used explicitly and needs a backstop in case the value is mistakenly not finalized through the trait.

As in the case 2 in the OP, the finalizing operation may be unsafe, meaning that the caller has to meet certain guarantees to avoid undefined behavior, which needs to made evident in code by an unsafe block or fn. Doing unsafe things unchecked in `Drop` is a recipe for disaster.

> [@](#):
>
> The other potentially interesting approach in my mind is to recast this as saying that a type cannot be dropped

That can only be practical in abort-on-panic setups, I'm afraid.

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [June 29, 2019, 3:25pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/8 "2019-06-29T15:25:41Z")

</div>

The relevant point from the blog post is about how this interacts very poorly with various kinds of generics, to the point that many transformations will diliute any value this would otherwise provide. For example, it is unclear how the compiler would appropriately deal with collections or options of `#[must_consume]` types, which seems like a big hole in the power of this guardrail.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 29, 2019, 8:30pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/9 "2019-06-29T20:30:40Z")

</div>

> [@mcy](#):
>
> it is unclear how the compiler would appropriately deal with collections or options of `#[must_consume]` types

The implementation of a generic collection needs to drop elements at some points, which would produce the lint when a `#[must_consume]` type is used as a parameter. I guess it would be painful to use `#[must_consume]` types in a general-purpose collection, but on the other hand, the question arises why one would want to do so. If the developers know what they are doing or simply don't care, they can always suppress the lint; it will serve to remind the readers of the code about the potentially improper use.

Options may be painful too, but above I have outlined an example of destructuring an option with some meaningful type conversion design.

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [June 29, 2019, 11:07pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/10 "2019-06-29T23:07:38Z")

</div>

> [@mzabaluev](#):
>
> I guess it would be painful to use `#[must_consume]` types in a general-purpose collection, but on the other hand, the question arises why one would want to do so.

The calculus is: why do you have such a high-maintenance type that requires such awful ergonomics? If you can't cram your cleanup into a destructor (which is what destructors are for...) I think you have bigger problems that a lint like this one will not save you from.

Phrased differently: if your type requires serious contortions to fit into otherwise general-purpose parts of the language, I suspect you have something that is either fundamentally unsafe (in a way Rust cannot protect you from) or a poor choice of abstraction (your example of "I don't want to make a network call inside of a destructor." indicates to me a very concerning model of resource acquisition).

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [June 30, 2019, 3:58am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/11 "2019-06-30T03:58:02Z")

</div>

I'd be happy to see a better abstraction for, e.g., `std::fs::File`. Or for my memory handle design of a custom allocation layer meant to be built upon by containers, that requires the caller to assert that use of the allocated buffer has really ceased at the point where the handle is released. I myself love my perfectly ergonomical RAII types when I can have them, but some of us have to deal with mean and unforgiving system stuff...

> [@mcy](#):
>
> I suspect you have something that is either fundamentally unsafe (in a way Rust cannot protect you from)

As long as it's not UB, I expect to be able to do it in Rust with the best ergonomics it can provide.

---

<div class="post-metadata">

### Author: ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)
#### Post date: [June 30, 2019, 9:28am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/12 "2019-06-30T09:28:55Z")

</div>

> [@mzabaluev](#):
>
> `Drop` cannot really do scary things like blocking because it can be invoked in an unwind. The proposed `Close` or `TryDrop` trait would need to be used explicitly and needs a backstop in case the value is mistakenly not finalized through the trait.
> 
> As in the case 2 in the OP, the finalizing operation may be unsafe, meaning that the caller has to meet certain guarantees to avoid undefined behavior, which needs to made evident in code by an unsafe block or fn. Doing unsafe things unchecked in `Drop` is a recipe for disaster.

Maybe the proposed semantics could be "only use `Drop` in case of a `panic!()`, otherwise don't allow `Drop` -- the author must somehow destructure the type", then?

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [June 30, 2019, 12:19pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/13 "2019-06-30T12:19:16Z")

</div>

> [@mzabaluev](#):
>
> `Drop` cannot really do scary things like blocking because it can be invoked in an unwind.

I don't get this. In the case of an unwind, you want to just leak your resources rather than attempt to close them gracefully? This seems wrong to me.

(If the argument is just "drop should be 'fast', such that unwinding is 'fast'", a) unwinding is programmer error and doesn't need to be fast, it's "kill this thread" level errors only, and b) that ship has already sailed with major libraries like rayon and crossbeam using drop points for synchronization.)

I think what you would benefit from is a `Drop` that does attempt to finalize the resource with _a_ handling of any errors (probably panicking or logging), and a by-value finalize that returns the error and `mem::forget`s to run the standard `Drop`.

For example:

```rust
struct RawHandle { .. }

pub struct Handle {
    raw: ManuallyDrop<RawHandle>,
}

impl Handle {
    fn close(mut self) -> Result<_, _> {
        let mut raw = ManuallyDrop::take(&mut self.raw); // still unstable
        mem::forget(self);
        // UFCS for clarity
        RawHandle::close(&mut raw)
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        RawHandle::close(&mut self.raw).map(drop).unwrap_or_else(|e| {
            if panicking() {
                log_error!("Failed to close handle during unwind; attempting to continue: {}", e);
            } else {
                panic!("Failed to close handle: {}", e);
            }
        });
    }
}

```

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [June 30, 2019, 3:50pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/14 "2019-06-30T15:50:16Z")

</div>

> [@mzabaluev](#):
>
> I’d be happy to see a better abstraction for, e.g., `std::io::File`

How is the RAII abstraction not good enough? Other languages basically add discount RAII (see, Java's try-with-resources, Python's with, etc) to deal with automatically closing fds.

> [@mzabaluev](#):
>
> but some of us have to deal with mean and unforgiving system stuff…

I don't agree that "system stuff" is mean and unforgiving-- I have found that RAII is a wonderful thing to have when writing ring0 code.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [July 1, 2019, 10:19am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/15 "2019-07-01T10:19:32Z")

</div>

> [@CAD97](#):
>
> I don’t get this. In the case of an unwind, you want to just leak your resources rather than attempt to close them gracefully?

Not necessarily, though "graceful" attempts should rather not try to affect the program state too much. Your example does more or less the right thing, except that `log_error!` should be implemented very carefully to not abort the program, and a panic on error may be not what the user wants to happen: it's also very common and legitimate to not care about close errors. A user-friendly way to solve this is to steer the API consumer into calling `Handle::close` and deciding what to do with the result. So there should be a way to discourage them from closing handles implicitly by dropping them.

> [@](#):
>
> (If the argument is just “drop should be ‘fast’, such that unwinding is ‘fast’”, a) unwinding is programmer error and doesn’t need to be fast, it’s “kill this thread” level errors only,

Perhaps the emphasis on "fast" is wrong here. In the main, there is a difference between "kill this thread" and "block this thread waiting for things to happen outside of the program before it can be killed", that I'm not comfortable with.

> [@](#):
>
> b) that ship has already sailed with major libraries like rayon and crossbeam using drop points for synchronization.)

That's legitimate if the synchronization is internal to the program (or, better, the specific library putting safe abstractions around it) and is expected to be resolved fast, assuming no deadlocks.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [July 1, 2019, 10:24am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/16 "2019-07-01T10:24:00Z")

</div>

> [@mcy](#):
>
> Other languages basically add discount RAII (see, Java’s try-with-resources, Python’s with, etc) to deal with automatically closing fds.

So what the Rust equivalent for that could look like? Numerous issues on [The Rust Programming Language · GitHub](http://github.com/rust-lang) have been filed and closed or postponed trying to offer such a language mechanism. The blog post you have kindly referenced earlier in this thread suggests that a strict form of enforcement would be both painful in practical use and not reliable enough to justify its strictness.

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [July 1, 2019, 11:00am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/17 "2019-07-01T11:00:34Z")

</div>

Expanding on how this could work with generic collections, dropping a `Vec<Handle>` would result in a `drops_must_consume` lint because the implementation of `Drop` for `Vec` uses `ptr::drop_in_place`. It would be nice if this did not: `vec.into_iter().for_each(|h| { let _ = h.close(); });` This, however, can’t be proven at compile time to not drop items outside of `Handle::close`, and indeed if a call to the latter panics, the remaining items will be dropped.

---

<div class="post-metadata">

### Author: ![Ixrec](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ixrec/32/6754_2.png) [@Ixrec](https://internals.rust-lang.org/u/Ixrec)
#### Post date: [July 1, 2019, 11:04am UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/18 "2019-07-01T11:04:32Z")

</div>

> [@mzabaluev](#):
>
> Perhaps the emphasis on “fast” is wrong here. In the main, there is a difference between “kill this thread” and “block this thread waiting for things to happen outside of the program before it can be killed”, that I’m not comfortable with.

Is "things outside of the program" directed at stuff like network operations, or stuff like (non-network) system calls? The running example has been closing files, which seems like (non-network) system calls, but I usually consider the operating system to be not "outside" the program so much as "underneath" it, like the Rust language and the memory allocator and the CPU and so on.

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [July 1, 2019, 3:16pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/19 "2019-07-01T15:16:27Z")

</div>

To clarify: the feature other languages go to lots of effort to sort-of add to deal with files are dtor-like mechanisms to implement something-like-RAII. RAII is a first-class feature in Rust that drives many of its abstractions (and, frankly, the only reasonable way to deal with resource management).

---

<div class="post-metadata">

### Author: ![mzabaluev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mzabaluev/32/3082_2.png) [@mzabaluev](https://internals.rust-lang.org/u/mzabaluev)
#### Post date: [July 1, 2019, 3:35pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/20 "2019-07-01T15:35:37Z")

</div>

> [@mcy](#):
>
> To clarify: the feature other languages go to lots of effort to sort-of add to deal with files are dtor-like mechanisms to implement something-like-RAII.

In both examples you cited the language runtime has exception handling facilities to gracefully deal with the situation when resource-finalizing code fails. What could be the Rust equivalent for that?

---

<div class="post-metadata">

### Author: ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)
#### Post date: [July 1, 2019, 3:44pm UTC](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485/21 "2019-07-01T15:44:33Z")

</div>

Rust does not have an exception-handling mechanism, full stop, so this question does not make sense. Those other languages crammed RAII into their exception-handling syntax because they had `finally`, which serves a similar purpose. Rust does not have (or need, because we have RAII) `finally`.

I think this thread has derailed, since I think we’re very far away from anything having to do with linear types.

[Next page](https://internals.rust-lang.org/t/idea-attributes-to-warn-if-value-is-dropped-without-being-acted-upon/10485.md?page=2)
