Skip to content
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

Add provider API to error trait #98072

Merged
merged 4 commits into from
Jul 14, 2022
Merged

Conversation

yaahc
Copy link
Member

@yaahc yaahc commented Jun 13, 2022

Implements rust-lang/rfcs#2895

@rust-highfive
Copy link
Collaborator

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Jun 13, 2022
@rust-highfive
Copy link
Collaborator

r? @kennytm

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 13, 2022
@yaahc
Copy link
Member Author

yaahc commented Jun 13, 2022

@rustbot label +T-libs-api -T-libs

@rustbot rustbot added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 13, 2022
@rust-log-analyzer

This comment has been minimized.

library/std/src/error.rs Outdated Show resolved Hide resolved
library/std/src/error.rs Outdated Show resolved Hide resolved
/// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
/// req
/// .provide_ref::<MyBacktrace>(&self.backtrace)
/// .provide_ref::<dyn std::error::Error + 'static>(&self.source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you foresee people doing this? I'd probably discourage it as it's replicating Error::source.

I could also see people doing .provide_ref::<SourceError> as a way of getting the source error and downcasting it in one step instead of two.

Copy link
Member Author

@yaahc yaahc Jun 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you foresee people doing this? I'd probably discourage it as it's replicating Error::source.

In the original PoC impl of error in core I thought it might be a good idea to deduplicate all of the provide style APIs to encourage a single point of interaction with the error trait, we could implement source in terms of provide by default, here's the old version of that:

    fn source(&self) -> Option<&(dyn Error + 'static)> {
        core::any::request_by_type_tag::<'_, core::any::tags::Ref<dyn Error + 'static>, _>(self)
    }

So you'd still default to using source as the getter but you could implement it via either method. I'm not sure if this is a good idea but I think it's worth considering. The biggest concern I can think of is that it will be easy to provide the source as the wrong type, similar to what you mentioned, someone could be trying to provide a trait object but forget to add the type parameter field and just write .provide_ref(&self.source). This could be improved a bit by adding an extra helper on Demand for .provide_source which is hard coded to dyn Error, but that seems a bit hacky given Demand will probably see usage in APIs unrelated to the error trait.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see a problem with providing it via both, although it's somewhat odd. I think you might want to provide it as the concrete type in more cases? Regardless, I think landing this will give a chance for idioms to settle.

/// impl std::error::Error for Error {
/// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
/// req
/// .provide_ref::<MyBacktrace>(&self.backtrace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This turbofish is redundant, I believe, so I'd recommend removing it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples all use turbofish unconditionally to reduce the chance of accidentally erasing the member as an unexpected type, with the turbofish you'll get a compiler error if you don't provide that exact type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accidentally erasing the member as an unexpected type

How would that occur? My understanding is that without the turbofish, the compiler will only be able to use the concrete type of the argument for type inference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would that occur? My understanding is that without the turbofish, the compiler will only be able to use the concrete type of the argument for type inference.

by providing a type that is different from what you intended / expected, the example I gave in the other comment on source is the best example I have off of the top of my head. If you mean to provide something as a trait object but you don't turbofish the trait object type or remember to manually coerce it you'll end up providing the original unerased type, and then any attempt to request the trait object will fail at runtime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you'll end up providing the original unerased type

Right, but this is "accidentally forgetting to erase" while you said (emphasis mine)

accidentally erasing the member as an unexpected type

Copy link
Member Author

@yaahc yaahc Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aah, yea, I overused erasing. In the first comment I was referring to .provide_ref as if it erased the T that is being provided, but in reality its the Demand that is the type erased memory location where the T is stored, and provide_ref is acting more like downcast on Any.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I've played around with this API, I've always used explicit turbofish, and think it's a good practice so that you know what you're providing.

/// let backtrace = MyBacktrace::new();
/// let source = SourceError {};
/// let error = Error { source, backtrace };
/// let dyn_error = &error as &dyn std::error::Error;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the choice of dyn Error here? Perhaps the idea is to demonstrate that it works on concrete types and trait objects? If so, I might encourage doing that more explicitly:

let a = error.request_ref::<MyBacktrace>().unwrap();
assert!(core::ptr::eq(&error.backtrace, a));

let dyn_error = &error as &dyn std::error::Error;
let b = dyn_error request_ref::<MyBacktrace>().unwrap();
assert!(core::ptr::eq(a, b));

or can you not call request_ref on a concrete type? If so, that'd make me sad.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or can you not call request_ref on a concrete type? If so, that'd make me sad.

Not until we move error into core sadly, for now I had to impl the Provider trait directly on dyn Error in order to make coherence treat it as a foreign trait on a local type. impl<E: Error> Provider for E produces a compiler error complaining about a blanket impl on a foreign trait.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not until we move error into core sadly

OK, if it can happen then, I'll be happy.

@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 3, 2022
@rustbot
Copy link
Collaborator

rustbot commented Jul 11, 2022

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@thomcc
Copy link
Member

thomcc commented Jul 12, 2022

r? @thomcc

@rust-highfive rust-highfive assigned thomcc and unassigned kennytm Jul 12, 2022
Copy link
Member

@thomcc thomcc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great to me! No real issues to speak of, and can't wait to start playing with it.

/// impl std::error::Error for Error {
/// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
/// req
/// .provide_ref::<MyBacktrace>(&self.backtrace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I've played around with this API, I've always used explicit turbofish, and think it's a good practice so that you know what you're providing.

/// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
/// req
/// .provide_ref::<MyBacktrace>(&self.backtrace)
/// .provide_ref::<dyn std::error::Error + 'static>(&self.source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see a problem with providing it via both, although it's somewhat odd. I think you might want to provide it as the concrete type in more cases? Regardless, I think landing this will give a chance for idioms to settle.

@thomcc
Copy link
Member

thomcc commented Jul 13, 2022

@bors r+

@bors
Copy link
Contributor

bors commented Jul 13, 2022

📌 Commit 655d6e8 has been approved by thomcc

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 13, 2022
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 14, 2022
Rollup of 6 pull requests

Successful merges:

 - rust-lang#98072 (Add provider API to error trait)
 - rust-lang#98580 (Emit warning when named arguments are used positionally in format)
 - rust-lang#99000 (Move abstract const to middle)
 - rust-lang#99192 (Fix spans for asm diagnostics)
 - rust-lang#99222 (Better error message for generic_const_exprs inference failure)
 - rust-lang#99236 (solaris: unbreak build on native platform)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 2b17aa6 into rust-lang:master Jul 14, 2022
@rustbot rustbot added this to the 1.64.0 milestone Jul 14, 2022
@yaahc yaahc deleted the generic-member-access branch July 14, 2022 19:56
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Jul 17, 2022
…plett

add tracking issue to generic member access APIs

Missed as part of rust-lang#98072
workingjubilee pushed a commit to tcdi/postgrestd that referenced this pull request Sep 15, 2022
add tracking issue to generic member access APIs

Missed as part of rust-lang/rust#98072
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants