-
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
Tracking issue for experiments around coercions, generics, and Copy type ergonomics #44619
Comments
While trying to generalize a concept in Rocket, I ran into issues when coercions I expected to happen automatically did not. I'll illustrate with examples. In the code below, the trait Handler { }
impl<F> Handler for F where F: Fn(&str) -> &str { }
fn is_handler<H: Handler>(_: H) { }
fn dyn_handler(_: &str) -> &str { "hi" }
is_handler(dyn_handler); On the other hand, none of the fn static_handler(_: &str) -> &'static str { "hi" }
is_handler(static_handler);
is_handler(|_| "hi");
is_handler(|x| x); In each of these cases, however, a manual coercion can be applied that results in the call typechecking: is_handler(static_handler as for<'r> fn(&'r str) -> &'r str);
is_handler((|_| "hi") as for<'r> fn(&'r str) -> &'r str);
is_handler((|x| x) as for<'r> fn(&'r str) -> &'r str); I expected that the coercions applied manually above would be applied automatically by Rust. In particular, I expected the But note that similar coercions are automatically applied when a function trait bound is enjoined without an accompanying fn mk_handler<F: Fn(&str) -> &str>(f: F) -> F { f }
is_handler(mk_handler(|_| "hi"));
is_handler(mk_handler(|x| x)); This implies that there already exists machinery to automatically perform these types of coercions in certain situations. I'd like to see these particular automatic coercions be applied during P.S: There are also cases where a manual coercion doesn't result in a typechecked call while an automatic coercion does: // does not typecheck
is_handler((|x: &str| x) as for<'r> fn(&'r str) -> &'r str);
// typechecks
is_handler(mk_handler(|x: &str| x)); And cases were neither works where it would likely be expected to: // does not typecheck
is_handler((|x: &str| -> &str { x }) as for<'r> fn(&'r str) -> &'r str);
// does not typecheck
is_handler(mk_handler(|x: &str| -> &str { x })); |
I am assuming you are talking about this example: is_handler(|_| "hi"); //~ ERROR
is_handler(mk_handler(|_| "hi")); // ok There are no coercions in either of these examples - the second one successfully uses the However, some specific examples are different, non-coercion problems: is_handler((|x: &str| x) as for<'r> fn(&'r str) -> &'r str); That is just #38714, which is being fixed is_handler(mk_handler(|x: &str| -> &str { x })); This also looks like some sort of screwiness, we might look into it. |
This appears to be true sometimes and not other times, but either way, my point is that (at least for function types) coercions should be trigged based on the set of pending trait obligations. For an instance of when these coercions do presently trigger, see the example towards the middle of my comment. I've reproduced it below: fn mk_handler<F: Fn(&str) -> &str>(f: F) -> F { f }
is_handler(mk_handler(|_| "hi"));
is_handler(mk_handler(|x| x)); |
Actually there are no coercions in any of these examples (except when explicitly triggered via casts), just heuristic HRT inference. |
Sorry, you're right; I was indeed referring to inference with respect to the closures. To rephrase, I meant to exhibit that the inferred type for |
For my own future searchability: this issue would address (certain cases of) the Eye of Sauron. |
I would also be nice to add a lint for autoref, not only auto-deref. Autoref can be dangerous too (when exact addresses matter, e.g. in FFI and memory-mapped I/O) — and for me, "dangerous" is far worse than "degrades performance" as a first approximation. Personally I would never want to use either the autoref or the autoderef feature, and I'd like the compiler to scream at me if it still ever happens by accident. |
interesting issue, my wish is for auto-borrows, (coming from C++ operator behaviour). I'm having to bounce between methods, operators, copy types with a number of conflicting draws (with C++ giving the case I actually want). it's not just that "it's that way in c++" - we had the ability to write anything (unchecked move or borrow) in C; whilst people complain about C++ references, sane operators was actually their driving use case. personally I would hope it's possible to get the best of both worlds perhaps with a As I see it moves or borrows are different preferences based on situations; I've never wanted maths to consume via moves, wheras control structures for threads & inserting items into collections are a clear move use-case. Also without overloading it should be easier to determine what to do from the function name (the compiler does seem to know the alternative in its error messages alot of the time) explicit mutable borrows are ok as you want to see the side effects. Besides that there's an intuition/readability aspect - mixing &'s and +'s hurts alot (and people must likely move back & forth between C++/rust.. I can't afford to throw away my fluency with the former) because you've learned to expect those operators to have certain meaning in positions, when combined like that it just looks like an error (& is associated with bitwise arithmetic or generating addresses when not, + near addresses is associated with pointer arithmetic.. , it's too much ). it was less of a pain to revert to named functions ("a.vadd(&b)"). I would have thought it would be far more sensible all round to keep the expectation of operators the same as C++ and resort to named functions for anything else (e.g. + is sometimes abused for concatenation which has different semantics to arithmetic.. better to use a named function there if you don't want to go the custom-operator route) Also originally I thought this was more about the operators but I realise now (from a previous 'bounce' in my sourcebase) that it's about the lack of auto-borrow generally (autoborrow exists for the receiver and is fine there). you can apply a concept like 'linear interpolation' to some quite weighty objects, but it would be nice to keep the same interface as for vectors.. it's a sliding scale. It creates distress relying on unstated semantics r.e making maths-types 'copy' ("hope the compiler will turn it into a reference..") which also narrows the use cases. (having delved into AI i want to keep my graphics maths general enough for n-d vector, which don't want to be 'copy'). I want to just implement operators for borrows, and have those work, rather than have to implement &T op T T op &T etc etc. Since I discovered Rust 5 years ago I originally went for named methods for maths , because the borrows were less obtrusive (and not all the overload permutations could be expressed) ; I was happy to wait and see how things evolved but unfortunately I think operators went the wrong way.. but it should be possible to fix with a backwards compatible preference |
to throw an idea out there, |
So, visiting this for triage; none of this ended moving forward, right? What do we do about this ticket? |
Hello from triage! Could someone summarize what has or hasn't been decided upon? |
We discussed this in the @rust-lang/lang meeting today. We'd still like to see many of these things happen, and we'd welcome project proposals, but we need to narrow the scope to specific proposals. In the meantime: @rfcbot postpone |
Team member @joshtriplett has proposed to postpone this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I'd definitely like to see experimentation in these areas -- I still like the "discarding ownership" idea -- but realistically it doesn't seem like anything is happening right now so this issue isn't providing value, so ☑. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
Like @scottmcm I am happy to close this issue, but I am still in favor of most of the ideas here. I think the right way forward at this point is probably experimenting with implementation. I don't know how much bandwidth there is for such mentoring, but I'd encourage folks to reach out. I would also say that filing lang team proposals for specific ideas here would be great, although I think the response will largely be "we're interested but it's going to require some experimentation!" |
This draws mostly on readily available links in rust-lang/rust#44619.
This draws mostly on readily available links in rust-lang/rust#44619.
Closing in favor of rust-lang/lang-team#62 and rust-lang/lang-team#63, as discussed in a recent language team meeting -- but if someone disagrees with that, please feel free to reopen. |
This draws mostly on readily available links in rust-lang/rust#44619.
This issue tracks experimental work, overseen by the lang team, on several interrelated topics:
Copy
types and references (i.e. "imagine never having to write&0
orlet z = &u * &(&(&u.square() + &(&A * &u)) + &one);
again)Copy
types.Clone
.None of these bullet items should be taken as signifying a decision, but rather a desire to experiment in a particularly tricky design space.
Ongoing experiments
Copy
TypesRelevant previous discussions
This tracking issue is a good place to continue discussion, at least initially; we may ultimately want to break out threads on internals to help hash out preliminary design thoughts.
After an experimental period, the expectation is that whatever designs are deemed sufficiently plausible are turned into fresh RFCs, and then go through the standard process.
The text was updated successfully, but these errors were encountered: