Impl trait contract#11
Conversation
|
i'll take a look today. i think there's active work around "negative impls", which use "!", in the language: https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types/topic/Negative.20impls.20work/near/407068626 so maybe the "!" was allowed to break ground / future proof for that. |
jct-tympanon
left a comment
There was a problem hiding this comment.
looks good! just one request (for doc), and one suggestion.
w.r.t. where clauses. it occurs to me that blanket implementations are something that can exist in the implementation rather than the contract. we make a static assertion that an impl exists with the contract; that impl may be achieved by direct declaration OR a blanket implementation in the loaded module. for that reason i think it might actually be inappropriate to make a "where clause" contract requirement. in any case, i suspect we won't miss them that much.
i am curious whether static assertions will work when the impl comes from a blanket implementation. i can't think of why they wouldn't. can you try that out in one of the examples? I think that would actually be a useful property to document / present via example, and a good test case.
There was a problem hiding this comment.
can you add an impl declaration to the code example in the doc? i think it's useful to demonstrate this syntax. i know we've got it in the examples but the rustdoc has a lot of value.
There was a problem hiding this comment.
Added. It'll fail the doctest build, as I know we have a separate issue to make the doctest build work. As an aside, I know you assigned that task to yourself. If you're finding yourself busy with other tasks, you could reassign that to me and I could pick it up.
There was a problem hiding this comment.
if you've got the time -- go for it.
|
I did confirm that that static assertions does work with blanket implementations. The following code passed as an implementation. I can change the |
I don't know if I was clear in my intent, but the specific idea was to do a blanket implementation over a "where" clause, where the impl contract was more specific. #[platform_spi]
example {
impl SomeTrait for MyRequiredType {}
}
/// somewhere deep inside one of the platform modules
impl<T> SomeTrait for T where T: SomeBoundaryThatIncludesMyRequiredType {}
// the above should be sufficient to make static assertion happy? |
|
Got it. Here's a deeper check including a where clause that passes: If you don't implement ToString here static assertions will fail, so it behaves as you'd expect it should. |
nice! i was thinking that 'where' clauses remain useful (and available) in implementation even if we don't support them in the contract specification. it may be the case that this is a useful limitation, since the true scope of a 'where' declaration at the contract level may be hard to reason about when you're considering many different, mutually exclusive platform implementations. instead the contract just declares narrow invariants about explicit, known types. "you must define this named type; this named type must implement this named trait". how those invariants are actually achieved (via direct implementation or blanket implementation) can be different in each platform-specific module. |
| /// pub use ErrorImpl as PlatformError; | ||
| /// | ||
| /// /// Trait contract that specifies that each platform-specific PlatformService will implement SomeTrait | ||
| /// impl SomeTrait for PlatformService; |
There was a problem hiding this comment.
should be PlatformService {}
There was a problem hiding this comment.
if only it wasn't...
I just remembered -- there are some established use cases for "!" like removing incorrectly computed autotraits like Send and Sync /// explicitly declare that MyType cannot be safely referenced across threads, because for some reason the compiler thinks
/// that it can, and I know better. i'm probably doing something weird, which is not unheard of
impl !Sync for MyType {} |
|
Interesting. Maybe this means that using "!" is a valid use case if it's more for convention. I'm not sure how that would play with the negative impls use case (those look like they use the same syntax with the "!" before the impl identifier, but the Rust reference shows "!" after the generic param declaration, so I'm not sure which syntax means what). I'd say we should forbid using it for now until it becomes more clear what "!" means in what contexts. |
A couple notes and limitations:
I disallow the usage of
!in the impl contract clause. This character is allowed per both the rust reference and inside ofsyn, but I can't for the life of me figure out what it's used for (the reference doesn't give any examples, maybe this is some Rust pattern I'm unfamiliar with). Let me know if you know and if we should validly support it.Where clauses aren't allowed as there's no way I could find to validate them with static assertions.
I've also put this logic outside of the match clause for hoisting alias and use items since using an enum to manage multiple return types felt clunky, but if there's an idiomatic pattern for it you like I could move it in.