-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Improve wording of the drop_bounds
lint
#86747
Conversation
Like you said, it doesn't solve the issue, simply improve the wording. Emitting a warning for a perfectly valid code is always an error on the lint end. We can merge this rewording as is, but we still need to address the lint issue. |
That's debatable, at the very least. For instance, the I have also forgotten to bless the one UI test affected by my changes; this should be fixed now. |
The whole suggestion is weird to me — I also agree with @GuillaumeGomez here that this is an error in the lint — we should not suggest that the user did not mean this unless we are quite certain that is the case. I'm curious how common it is in practice for someone to add a |
That really is passing the buck, though. Why does the function need Drop bounds? |
In principle, I agree. But to quote from #86653:
i.e. in this particular sense of "usefulness", the |
I don’t think the bounds imposed on the trait implementation are the problem, but the function is. You could write it like this, instead, at no loss of power: trait Placeholder {}
impl<T> Placeholder for T {}
pub fn bar(_: *mut dyn Placeholder) {} Which means the lint should probably warn on |
I'm not sure I follow why |
The problem is that types might be droppable without implementing Drop. The standard String type is an example. The trait with a blanket impl doesn’t have this problem. String doesn’t implement Drop, but it does implement Placeholder. |
That's a good point. I still feels unfortunate though. In some sense, it feels like any type should be castable to a |
Based on the conversation in rust-lang#86747. Explanation ----------- A trait object bound of the form `dyn Drop` is most likely misleading and not what the programmer intended. `Drop` bounds do not actually indicate whether a type can be trivially dropped or not, because a composite type containing `Drop` types does not necessarily implement `Drop` itself. Naïvely, one might be tempted to write a deferred drop system, to pull cleaning up memory out of a latency-sensitive code path, using `dyn Drop` trait objects. However, this breaks down e.g. when `T` is `String`, which does not implement `Drop`, but should probably be accepted. To write a trait object bound that accepts anything, use a placeholder trait with a blanket implementation. ```rust trait Placeholder {} impl<T> Placeholder for T {} fn foo(_x: Box<dyn Placeholder>) {} ```
feat(rustc_lint): add `dyn_drop` Based on the conversation in rust-lang#86747. Explanation ----------- A trait object bound of the form `dyn Drop` is most likely misleading and not what the programmer intended. `Drop` bounds do not actually indicate whether a type can be trivially dropped or not, because a composite type containing `Drop` types does not necessarily implement `Drop` itself. Naïvely, one might be tempted to write a deferred drop system, to pull cleaning up memory out of a latency-sensitive code path, using `dyn Drop` trait objects. However, this breaks down e.g. when `T` is `String`, which does not implement `Drop`, but should probably be accepted. To write a trait object bound that accepts anything, use a placeholder trait with a blanket implementation. ```rust trait Placeholder {} impl<T> Placeholder for T {} fn foo(_x: Box<dyn Placeholder>) {} ```
@FabianWolff @GuillaumeGomez What's the status of this? |
The initial problem I pointed out is still there. |
And so is my objection to it. With #86848, both the function and the trait from the example in #86653 now get a warning — rightly so, because both uses of the My rewording tries to reflect this (without even claiming to fix #86653) by making the explanatory text for the lint slightly more conservative and less focused on the "usefulness" of a @rustbot label: +S-waiting-on-review -S-waiting-on-author |
Indeed, my bad. @bors: r+ rollup |
📌 Commit 644529b has been approved by |
…laumeGomez Rollup of 7 pull requests Successful merges: - rust-lang#86747 (Improve wording of the `drop_bounds` lint) - rust-lang#87166 (Show discriminant before overflow in diagnostic for duplicate values.) - rust-lang#88077 (Generate an iOS LLVM target with a specific version) - rust-lang#88164 (PassWrapper: adapt for LLVM 14 changes) - rust-lang#88211 (cleanup: `Span::new` -> `Span::with_lo`) - rust-lang#88229 (Suggest importing the right kind of macro.) - rust-lang#88238 (Stop tracking namespace in used_imports.) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
(Commenting on this issue and not #86848, because it seems to be where the relevant parties are.) Here's my code example that hits this, in which I conditionally return a guard object or a match filename {
Some(filename) => {
let (layer, flush_guard) = ChromeLayerBuilder::new()
.file(filename)
.include_args(should_include_function_args)
.build();
(Some(layer), Box::new(flush_guard))
}
None => {
struct TrivialDrop;
impl Drop for TrivialDrop {
fn drop(&mut self) {
// Do nothing.
}
}
(None, Box::new(TrivialDrop))
}
} Warning messageI'm not sure if I'm missing something, but the warning message doesn't mention creating a placeholder trait. There's an explanation for the lint in the source code, but I don't know the error code, and it doesn't appear in the error index, and the compiler output message doesn't say how to explain the lint (playground):
So it was pretty hard to figure out what the issue was. I also think the Output position?Should the 1. The output type doesn't need to "accept" an entire class of input types, in contrast to the discussion in the lint justification. The caller can check neither at compile-time nor at run-time if the type is trivially-droppable. 2. In my case, I feel like the code is a lot clearer with
In my case, it's not really even the case that the drop should be "deferred". The caller is responsible for managing it, just like any other resource it manages, which is to say that it should probably be dropped at the end of the scope it's created in:
In the first scenario, the reader has to do some research, but the signature makes sense on the first read. In the second scenario, the reader is initially surprised. I think we should avoid the scenario where the extra trait is not just boilerplate, but actively surprising. 3. Using a name like 4. It does seem like you can currently write a function which returns // Doesn't trigger a warning.
fn returns_a_guard() -> impl Drop { ... } |
First of all, I recommend using fn install_tracing() -> eyre::Result<impl Drop> {
// You have to make sure you cast `as Box<dyn Drop>`, because Rust won't do it automatically
Ok(Box::new(droppable) as Box<dyn Drop>)
}
But yes, it's an oversight. |
Thanks for the tip. Returning |
This PR addresses #86653. The issue is sort of a false positive of the
drop_bounds
lint, but I would argue that the best solution for #86653 is simply a rewording of the warning message and lint description, because even if the lint is technically wrong, it still forces the programmer to think about what they are doing, and they can always use#[allow(drop_bounds)]
if they think that they really need theDrop
bound.There are two issues with the current warning message and lint description:
Drop
bounds are "useless", which is technically incorrect because they actually do have the effect of allowing you e.g. to call methods that also have aDrop
bound on their generic arguments for some reason. I have changed the wording to emphasize not that the bound is "useless", but that it is most likely not what was intended.std::mem::needs_drop
detects whether a type has a destructor. But I think this is also technically wrong: TheDrop
bound says whether the type has a destructor or not, whereasstd::mem::needs_drop
also takes nested types with destructors into account, even if the top-level type does not itself have one (although I'm not 100% sure about the exact terminology here, i.e. whether the "drop glue" of the top-level type counts as a destructor or not).cc @jonhoo, does this solve the issue for you?
r? @GuillaumeGomez