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

Support specializing on trait #3

Open
kangalio opened this issue Jan 4, 2022 · 11 comments
Open

Support specializing on trait #3

kangalio opened this issue Jan 4, 2022 · 11 comments
Labels
enhancement New feature or request

Comments

@kangalio
Copy link

kangalio commented Jan 4, 2022

Auto-deref specialization can also be used to specialize on trait implementations. I'm using this a lot in a lib (1, 2, 3) and it would be awesome to abstract it away into a crate like castaway.

Here's a small proof of concept macro link (only supports matching on type directly so far)

@jplatte
Copy link

jplatte commented Jan 4, 2022

Just my 2c, but I don't think the macro stuff should be in the same crate as typeid-based specialization. They work very differently and thus have different areas where they are applicable.

@sagebind
Copy link
Owner

sagebind commented Jan 4, 2022

Thanks for the suggestion! This is something I'd definitely be open to adding to Castaway as long as it can be done in an expression context. The general design for Castaway is for casting to be done in expression position and not item-level position -- there are probably other crates out there if you want to simply write multiple specialized implementations of a trait.

Just my 2c, but I don't think the macro stuff should be in the same crate as typeid-based specialization. They work very differently and thus have different areas where they are applicable.

Actually I'm not totally opposed to macro-based operations. Castaway itself already leverages autoref-based macro specialization for handling reference types as well as type ID checks, as I find a combination/mix gives the best results. But my main concern would be doing things in expression position; Castaway should not be generating traits or their implementations except as necessary for internal use.

If Castaway were to support trait-based / generic casting, I would expect it to look more like this:

fn maybe_debug<T: 'static>(value: T) -> Option<String> {
    if let Ok(debuggable) = cast!(value, impl Debug) {
        Some(format!("{:?}", debuggable))
    } else {
        None
    }
}

Though I am doubtful that this would be possible; we'd have to generate some type for debuggable to resolve to which wouldn't be solvable except for maybe a trait object (which dynamic dispatch should not be present in Castaway). I'll think a bit more on possible APIs.

@jplatte
Copy link

jplatte commented Jan 4, 2022

Though I am doubtful that this would be possible

It isn't, autoref specialization only works when the concrete type is actually known (i.e. when the generics you'd usually use are replaced by macros). See dtolnay/case-studies.

@NobodyXu
Copy link
Contributor

NobodyXu commented Apr 4, 2022

Though I am doubtful that this would be possible; we'd have to generate some type for debuggable to resolve to which wouldn't be solvable except for maybe a trait object (which dynamic dispatch should not be present in Castaway). I'll think a bit more on possible APIs.

I think once that Tracking issue for impl Trait in const and static items and let bindings #63065 is merged in, we will be able to this trivally by simply declaring let var: impl Trait = ....

@sagebind
Copy link
Owner

sagebind commented Apr 4, 2022

@NobodyXu That may require some experimentation, but my instinct tells me that impl Trait won't help us here. The reason why is that we can't guarantee that the existential will be constrained. For example, in the failure case:

struct S;

if let Ok(display) = cast!(S, impl Display) {
    Some(format!("{}", display))
} else {
    None
}

Struct S does not implement Display. When this code compiles, what we want it to compile to is an Err since the implementation check fails, but our existential declaration of impl Display will not be bound to anything. This will result in a compile-time error because the existential is unconstrained. The only way to avoid this would be to not generate the existential unless we already knew that the given type implemented the trait.

@NobodyXu
Copy link
Contributor

NobodyXu commented Apr 5, 2022

@NobodyXu That may require some experimentation, but my instinct tells me that impl Trait won't help us here. The reason why is that we can't guarantee that the existential will be constrained. For example, in the failure case:

struct S;

if let Ok(display) = cast!(S, impl Display) {
    Some(format!("{}", display))
} else {
    None
}

Struct S does not implement Display. When this code compiles, what we want it to compile to is an Err since the implementation check fails, but our existential declaration of impl Display will not be bound to anything. This will result in a compile-time error because the existential is unconstrained. The only way to avoid this would be to not generate the existential unless we already knew that the given type implemented the trait.

Yes indeed, I didn't think of that.

Perhaps we can do that with match_type! though, since we can simply ignore the unmatched branches when generating code?

@sagebind
Copy link
Owner

sagebind commented Apr 5, 2022

match_type! doesn't do any conditional compilation; it could be thought of it as being more similar to if cfg!() {} as opposed to #[cfg()] {}. All branches are present and checked by the compiler, and only during later stages of compilation does rustc identify the only possible branch and discard the other branches.

@NobodyXu
Copy link
Contributor

NobodyXu commented Apr 5, 2022

match_type! doesn't do any conditional compilation; it could be thought of it as being more similar to if cfg!() {} as opposed to #[cfg()] {}. All branches are present and checked by the compiler, and only during later stages of compilation does rustc identify the only possible branch and discard the other branches.

Oops, I forgot about that.

If and only if rust support if constexpr like C++ does, then things will be a lot easier.

@sagebind
Copy link
Owner

sagebind commented Apr 5, 2022

I'm not sure even then would this help. The problem is that in Rust, traits are not considered types (unlike interfaces in many languages). So you can't actually "compare" a type with a trait in any meaningful sense, you can only constrain generic bounds using them. So ultimately the compiler would need to provide some sort of way of checking bounds outside of the normal means of a generic parameter, which I doubt will happen. Or provide specialization directly, at which point it is possible to do some trickery to get it to work.

@NobodyXu
Copy link
Contributor

NobodyXu commented Apr 5, 2022

I'm not sure even then would this help. The problem is that in Rust, traits are not considered types (unlike interfaces in many languages). So you can't actually "compare" a type with a trait in any meaningful sense, you can only constrain generic bounds using them. So ultimately the compiler would need to provide some sort of way of checking bounds outside of the normal means of a generic parameter, which I doubt will happen. Or provide specialization directly, at which point it is possible to do some trickery to get it to work.

Providing direct support of specialization would definitely make this possible and effectively deprecate this crate.

But looking at the PR, it is going to take a long time before it happens.

@sagebind
Copy link
Owner

sagebind commented Apr 5, 2022

Yep, very true. And now you're up to speed as to why Castaway doesn't currently support casting to traits. 😀 Unless some other novel implementation strategy is discovered.

@sagebind sagebind added the enhancement New feature or request label Apr 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants