If a language version (target) is introduced I believe it should be crate specific, aka mixing crates using different language versions should be fine. I would even consider making it module! specific (or having some major.minor scheme where all modules in the same crate have to have the same major lang version, but possible different minor lang versions)
I think such a language version should be for some new features which introduce some syntactical only breaking changes . Also this could be extended in “some” degree to API changes, as long as this won’t lead to incompatibilities with existing libraries using a older language version. Note that I mainly mean incomparability of using older libraries in libs/progs using a newer language version. I think the other way around it is fine as long as the library still can be used, through maybe not some of it’s part’s (e.g. parametrized modules or some other strange, but probably use full, thinks).
This can be used to introduce keywords which change crate/module/function internal aspects only, like catch. This could also be used to “phase out” some other part’s, which else wise would, at most, generate warning, e.g. maybe the (in the future) old macro system (<- not completely sure about this myself).
Through I think it should not be called language version we already have a language version i.e. the rust© version. Maybe some think like syntax version might be a better name (but then if might actually be a bit more then syntax…).
Oh and add syntax_version = <newest> to new crates and additionally make sure “nice” error messages are produced when accidentally colliding with “old” syntax versions.
Alternative a #[syntax_features=...] crate/module(/function?) level annotation can work too (including stable).
Lastly do catch { ... } is not “that” bad 