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

impl From<FlagSet<F>> for F::Type? #20

Open
EndilWayfare opened this issue Oct 22, 2021 · 1 comment
Open

impl From<FlagSet<F>> for F::Type? #20

EndilWayfare opened this issue Oct 22, 2021 · 1 comment

Comments

@EndilWayfare
Copy link

Obviously, from a pure-Rust standpoint, I think it makes sense to stay in the typecheck-guaranteed land of FlagSet: just think in terms of "a set of flags", and "it's a bitfield" is just an implementation detail. If you're converting to an integer, you better have a good reason.

I'm coming from the interop/FFI side of things, where I really dislike the type-looseness of winapi's "ayyy ENUM is just a bunch of constants and a type alias. Shove a STGMOVE into a VARENUM? Sure, that's allowed, actually defined, though semantically absurd. Shove a VARENUM into a STGMOVE? That's undefined, but we're not gonna stop you lmao". I can't imagine seriously going back to a language without proper sum types. The "good reason" for casting to integer, though, is "that's what the foreign API expects".

Sure, the bits method exists, but I think there's something to be said for the universally understood semantics of From/Into. Going from FlagSet<F> -> F::Type is infallible, so I can't think of a good reason off the top of my head not to impl.

@EndilWayfare
Copy link
Author

Ok, counter-argument might be

fn foo(bar: impl Into<FlagSet<Bar>>) {
    unsafe {
        foreignFoo(bar.into().bits())
    }
}

is the most clear, compact way to delineate "convert compatible Rust type to FlagSet; single variants trivially fungible to FlagSets" from "convert definitely-actually-a-FlagSet-at-this-point to the integer the foreign function is expecting".

Even if rustc is smart enough to understand what you mean by bar.into().into(), that's pretty ugly and less immediately clear to human readers.

Some more tortured construct like

let bar: FlagSet<_> = bar.into()
unsafe {
    foreignFoo(bar.into())
}

seems unnecessarily inelegant. Like, the category-theoretic allure of "From as universal interface" is tarnished by the contortions you have to conjure to make it work; it's supposed to be more clear and beautiful, so this is only counterproductive.

Dang it guys, sorry. Looks like, if I think about it harder, I'm actually sad that we don't have sufficient specialization stabilized to express something blanket like

impl<F, T> From<F> for T
where:
    F: Into<Flags<Type = T>>

Ugh. "Transitive From" is something I often find myself wishing for, even though I realize the edge cases are necessarily extremely hairy. Thank god for type theory wizards that protect overly ambitious architects from spinning off into outer space of abstractions...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Development

No branches or pull requests

1 participant