-
Notifications
You must be signed in to change notification settings - Fork 88
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
Consider making types non_exhaustive #117
Comments
There is a |
Note that with private fields or |
I feel like there's no really good solution here. Backwards compatibility of the wire format does not translate nicely to the backwards compatibility of the Rust API (mostly, to due to the impedance mismatch between TypeScript's structural and Rust's nominal type systems). My current view is that, instead trying to share lsp types as a whole, crates that act as building blocks for LSP servers/clients should just vendor the relevant types. For example, my lsp-server crate does not use And yes, I totally did make a typo there once, which broke rust-analyzer, but, with the typo fixed, I feel like this code won't be changed in the future. Similarly, crates for diagnostics could vendor only diagnostics-related structs, and crates for VFS can vendor only change-notification related structs. In other words, I advocate for "worse is better" approach in this instance :) |
Yes, Some simple types like https://docs.rs/lsp-types/0.61.0/lsp_types/struct.Location.html could fairly reasonably be seen as types which won't have fields added and could be spared the non-exhaustiveness. After all even if they do get a field it is not the end of the world if a 2.x has to be released, we just need to make breaking releases unlikely enough to not be much of an issue. |
Since these types are inherently structural, maybe it'd make more sense to wait for rust-lang/rfcs#2584 instead? |
@Xanewok I don't see how would that help, what am I missing? Most types already have names in the LSP definition in any case. |
It isn't really structural vs nominal that is at play here, just that typescript (or in a way just plain JSON which is the actual data format) have data which are free to be extended with additonal fields or cases.
That works well in many cases such as that where the crate doesn't leak the LSP-types outside of its API (notably, lsp-server could just use an arbitrary version of It is always possible to vendor the types when needed, the question here is whether doing this change, which would make 1.0 possible, does not adversely affect crates using |
You're right - apart from structural types we'd need structural subtyping (which is, rather, what makes these TS-side definitions extendable IIUC), which I believe is out of scope in the RFC I linked previously. Sorry. |
I looked it this a bit, but there are types which have fields without a sensible default so we can't use Avoiding churn could perhaps be avoided by crates specifying a version range instead. Still forces some churn, but it would only be for crates like |
Yeah, I'm really thinking I want to avoid direct dependencies on |
Could you perhaps explain/give an example for what you mean by "specifying a version range instead" - I feel like I missed something there. |
In |
Ahhh, that is indeed handy! |
Yeah, I'd be pretty conservative with the version bounds, only bumping when asked, and only specifying it up to a currently released version. |
I'm wondering if we should put this in the README: "Note that due to frequent updates, we recommend library authors to specify a version range... (post example, with guidelines for how and when to update bounds)". cc. @ebkalderon |
This sounds like a good solution! I think this could be a great solution for |
I probably wouldn't use |
Tbh I still think there might be value in making the definitions in |
Makes sense to me, yeah. I like your idea of selecting a version range from a past version up to the currently released version. I think that could be a good strategy, provided that the number of types your library directly references or constructs is relatively small. |
I've made a PR on |
The need to make a breaking release every time a new field is added to a type causes a lot of churn in dependent crates (see for instance brendanzab/codespan#124).
We could reduce this significantly (perhaps completely) by making all enums and structs non exhaustive through adding an extra variant to all enums and an extra field to all structs which is not meant to be referred to directly.
Uses of any enums would then need to add a catch all case on each match which errors on unknown variants and use
Struct { a, b, .. }
when matching on structs. Further, constructing any struct would requireDefault::default()
+ functional record update which is perhaps the biggest ergonomic loss.I haven't decided yet if I think being able to move to 1.0 is worth the ergonomic loss but I wanted to float the idea here.
@Xanewok
@matklad
@brendanzab
@autozimu
(more?)
The text was updated successfully, but these errors were encountered: