Skip to content

Tracking issue for experiments around coercions, generics, and Copy type ergonomics #44619

@aturon

Description

@aturon
Member

This issue tracks experimental work, overseen by the lang team, on several interrelated topics:

  • Improving the ergonomics around Copy types and references (i.e. "imagine never having to write &0 or let z = &u * &(&(&u.square() + &(&A * &u)) + &one); again)
  • Improving the consistency of method dispatch for operators with standard dispatch.
  • Improving the ergonomics around using owned values where references are expected.
  • Improving the interaction between generics and coercions.
  • Improving the interaction between function-passing code and coercions.
  • Helping to avoid accidental copies of large Copy types.
  • Potentially a very limited form of auto-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

Relevant 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.

Activity

added
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
on Sep 18, 2017
SergioBenitez

SergioBenitez commented on Sep 28, 2017

@SergioBenitez
Contributor

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 Handler trait is implemented for all types that implement the function trait Fn(&str) -> &str (note the implicit HRTB). Among these types, of course, is for<'r> fn(&'r str) -> &'r str. The program below typechecks:

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 is_handler calls below typecheck:

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 'static lifetime in return types to coerce to a generic lifetime.

But note that similar coercions are automatically applied when a function trait bound is enjoined without an accompanying impl search (re: @eddyb). The following program typechecks without any manual coercions:

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 impl search as well, at least in the case of function types.

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 }));
arielb1

arielb1 commented on Sep 28, 2017

@arielb1
Contributor

I think what's going here is just that coercions, like everything else in Rust, don't trigger based on the set of pending trait obligations.

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 [closure]: Fn(&str) -> &str impl. The reason the first example doesn't work is because you only get 1 chance to trigger higher-ranked inference (I don't think this is going to get any better - this area is full of undecidable cases and is basically heuristic), and a _: Handler bound isn't enough to trigger it (while a _: Fn(&str) -> &str is).

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.

SergioBenitez

SergioBenitez commented on Sep 28, 2017

@SergioBenitez
Contributor

I think what's going here is just that coercions, like everything else in Rust, don't trigger based on the set of pending trait obligations.

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));
arielb1

arielb1 commented on Sep 28, 2017

@arielb1
Contributor

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.

Actually there are no coercions in any of these examples (except when explicitly triggered via casts), just heuristic HRT inference.

22 remaining items

nikomatsakis

nikomatsakis commented on Oct 12, 2020

@nikomatsakis
Contributor

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!"

Mark-Simulacrum

Mark-Simulacrum commented on Oct 20, 2020

@Mark-Simulacrum
Member

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.

added
to-announceAnnounce this issue on triage meeting
and removed
final-comment-periodIn the final comment period and will be merged soon unless new substantive objections are raised.
on Oct 22, 2020
added and removed on Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-type-systemArea: Type systemC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-langRelevant to the language teamWG-traits[RETIRED] Working group: Traitsdisposition-postponeThis issue / PR is in PFCP or FCP with a disposition to postpone it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @steveklabnik@spastorino@nikomatsakis@joshtriplett@shepmaster

        Issue actions

          Tracking issue for experiments around coercions, generics, and Copy type ergonomics · Issue #44619 · rust-lang/rust