-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Warn when lifetime parameters are unused. #5922
Comments
Nominating for milestone 3, feature-complete |
accepted for feature-complete milestone |
@thestinger why not just use |
This seems to work now: struct Foo<'a>; |
...but there seem to be issues: #10703 |
To expand on my previous comment, which was quite terse -- I see four options: Option 1. Leave things as they are. This means that unused lifetime parameters are a bit of a footgun, because people seem to expect them to behave in a contravariant fashion (as described by @thestinger and in #10703). Option 2. If inference finds that a lifetime parameter is bivariant (effectively, unused), default to contravariant, which seems to be what people expect. This means that a lifetime "can only get shorter", much like the lifetime For example, if I have Option 3. Report an error for bivariant lifetime parameters. This is unfortunate in that one can imagine legitimately wanting a bivariant lifetime parameter in some unusual cases. (The same is true of the previous case) Option 4. Add the ability to specify variance explicitly, and then report an error for bivariant lifetime parameters. This seems reasonable but requires people to know what "variance" means. I imagine a syntax like All of this applies equally to type parameters, incidentally. |
To be clear, I favor Option 4 at this point. |
@nikomatsakis For options 3 and 4, when is the error for bivariant lifetime parameters being reported? Is it an error to declare a bivariant parameter? Is it merely an error to instantiate a bivariant parameter? It is not clear to me what the value is of having an explicit syntax for an erroneous construct. Update from IRC: niko clarified that the error would be (I think) from inferring bivariant lifetime parameter. But an explicitly specified bivariant parameter would be allowed. |
Oh, and @pnkfelix says that one wrinkle of Option 4 was not clear: the idea was that it's only an error to we infer bivariance. If the user manually specifies bivariance for a type/lifetime parameter, that would be considered legal. |
If we want a syntax that is able to be generalized to type parameters, then we should probably not use any construct that is part of the grammar for type-expressions and trait-bounds, in order to avoid confusion for the users. That would remove |
I think I'm just in favour of Option 1 at this point. I don't think this is important enough to merit any additional type system complexity. It seems we just add The vector iterator uses raw pointers because the end pointer points one-past-the-end of the vector and the current pointer will end up being equal to |
@pnkfelix variance annotations don't conflict with the type grammar in any way. They would precede type parameter declarations. @thestinger I think I am wary of 1 in that it represents a footgun that has come up already twice. People will expect the type system to be giving them guarantees it's not and it's surprising. I might be in favor of rendering bivariance illegal combined with lang items as you describe, but that is pretty close to option 4 (explicit annotation). Variance annotations are not especially complex on the compiler side. Whether it presents a simpler face to the end user is I guess the question -- lang items are bit more obscure somehow. I guess I'm largely indifferent. |
@nikomatsakis: The place where you can screw this up is in code that's already using |
@nikomatsakis I did say "to avoid confusion for the users" ... but perhaps I am just paranoid. |
@thestinger you cannot violate the type system without unsafe code, but you can be confused about what guarantees you are getting. This is what happened to @Kimundi in #10703 |
My general feeling is that Option 4 is the best. I don't think most users are likely to hit this in practice, because most users aren't going to be adding bivariant lifetime parameters to their types (from what I've seen people generally only add lifetime parameters when the compiler yells at them for not having one). Making inferred bivariant parameters illegal and providing a way to explicitly annotate each parameter, while certainly more complex from a syntactical perspective, is also the clearest and least surprising approach. Incidentally, what's the benefit to allowing explicit bivariant parameters? |
I think lifetimes are already on the boundary of how much syntactical complexity most people will accept in a language. Adding any more qualifiers will push it over the edge. I can understand that it's problematic for the meaning of a lifetime bound to depend on how the type uses it internally, but that's already true of type parameters and whether a type is |
@thestinger Good point about type parameters and kinds. I'm worried, though, that there may be cases where someone wants a covariant type parameter without using it in a way that allows covariance to be inferred, if we default to contravariance. For example, FFI bindings may want to use covariant lifetime parameters to enforce restrictions that otherwise can't be inferred (because under the hood they use |
I don't have a solid opinion about which option is best, but I share @thestinger's concern about syntax. Pointers and lifetimes are cryptic enough already. If we go with 4 I think the variance annotations should be somewhat longer-form. I think that in general, the rule of thumb should be that verbosity is inversely proportional to ubiquity. If something is used all over the place you're likely to be aware of its meaning, and want it to be compact. If something is used rarely (as here) you don't mind if it's a bit longer, and since you quite possibly haven't ever seen it before, you benefit from it being self-descriptive. Just spelling out |
I was thinking about this issue and I realized that it's just a specific case of the more general problem of inference being unaware of special constraints that arise in unsafe code, and that we currently have a variety of ad-hoc mechanisms for addressing this problem. For example, we sometimes use annotations to inform the "builtin traits" ( |
This implies that the current |
I've added issue #10834 about centralizing on a single mechanism for these various related cases, and updated the title of this issue to indicate that we should at least warn when we infer bivariance. |
Also, unassigning myself. |
of builtin bounds. Fixes rust-lang#10834. Fixes rust-lang#11385. cc rust-lang#5922.
Assigning P-low. |
Triage: we now have lifetime markers, which seem like a satisfactory solution to this problem. Not sure if there's still a story here other than we would like to lint or error on unused parameters (if they are inferred to be bivariant). |
(I have a variance RFC coming up. Working on one nit with the implementation, but I should just post the RFC.) |
closing in favor of #22212 (the tracking issue for @nikomatsakis 's variance RFC mentioned above) |
Rustup r? @ghost changelog: none
UPDATE: Updating this issue to reflect current thinking, which is that inference should issue a warning when a lifetime parameter is inferred to be bivariant (which implies that the parameter is unused).
ORIGINAL: It's sometimes necessary to specify a lifetime on an object that doesn't contain borrowed pointers, because lifetime dependencies can be required for safety outside of Rust's borrowed pointer system. A dummy borrowed pointer member is a workaround, but wasteful.
@nikomatsakis
The text was updated successfully, but these errors were encountered: