From d3d8d44c2f64560cef515a9659f57b85ea632594 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 6 May 2024 03:50:54 +0200 Subject: [PATCH] Add two draft project goals: seamless C support, and relaxing the orphan rule Both WIP, both without owners yet. These are meant as samples of goals, and as samples of goals without owners specifically. However, they're also goals that people have expressed support for solving. --- src/2024h2/Relaxing-the-Orphan-Rule.md | 132 ++++++++++++++++++++++++ src/2024h2/Seamless-C-Support.md | 137 +++++++++++++++++++++++++ src/2024h2/slate.md | 6 +- 3 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/2024h2/Relaxing-the-Orphan-Rule.md create mode 100644 src/2024h2/Seamless-C-Support.md diff --git a/src/2024h2/Relaxing-the-Orphan-Rule.md b/src/2024h2/Relaxing-the-Orphan-Rule.md new file mode 100644 index 0000000..6839a51 --- /dev/null +++ b/src/2024h2/Relaxing-the-Orphan-Rule.md @@ -0,0 +1,132 @@ +# Relaxing the Orphan Rule + +| Metadata | | +| --- | --- | +| Owner(s) | | +| Teams | *lang* | +| Status | WIP | + +## Motivation + +Relax the orphan rule, in limited circumstances, to allow crates to provide +implementations of third-party traits for third-party types. The orphan rule +averts one potential source of conflicts between Rust crates, but its presence +also creates scaling issues in the Rust community: it prevents providing a +third-party library that integrates two other libraries with each other, and +instead requires convincing the author of one of the two libraries to add +(optional) support for the other, or requires using a newtype wrapper. Relaxing +the orphan rule, carefully, would make it easier to integrate libraries with +each other, share those integrations, and make it easier for new libraries to +garner support from the ecosystem. + +### The status quo + +Suppose a Rust developer wants to work with two libraries: `lib_a` providing +trait `TraitA`, and `lib_b` providing type `TypeB`. Due to the orphan rule, if +they want to use the two together, they have the following options: + +- Convince the maintainer of `lib_a` to provide `impl TraitA for TypeB`. This + typically involves an optional dependency on `lib_b`. This usually only + occurs if `lib_a` is substantially less popular than `lib_b`, or the + maintainer of `lib_a` is convinced that others are likely to want to use the + two together. This tends to feel "reversed" from the norm. + +- Convince the maintainer of `lib_b` to provide `impl TraitA for TypeB`. This + typically involves an optional dependency on `lib_a`. This is only likely to + occur if `lib_a` is popular, and the maintainer of `lib_b` is convinced that + others may want to use the two together. The difficulty in advocating this, + scaled across the community, is one big reason why it's difficult to build + new popular crates built around traits (e.g. competing + serialization/deserialization libraries, or competing async I/O traits). + +- Vendor either `lib_a` or `lib_b` into their own project. This is + inconvenient, adds maintenance costs, and isn't typically an option for + public projects intended for others to use. + +- Create a newtype wrapper around `TypeB`, and implement `TraitA` for the + wrapper type. This is less convenient, propagates throughout the crate (and + through other crates if doing this in a library), and may require additional + trait implementations for the wrapper that `TypeB` already implemented. + +All of these solutions are suboptimal in some way, and inconvenient. In +particular, all of them are much more difficult than actually writing the trait +impl. All of them tend to take longer, as well, slowing down whatever goal +depended on having the trait impl. + +### The next few steps + +As an initial experiment, try relaxing the orphan rule for binary crates, since +this cannot create library incompatibilities in the ecosystem. Allow binary +crates to implement third-party traits for third-party types, possibly +requiring a marker on either the trait or type or both. See how well this works +for users. + +As a second experiment, try allowing library crates to provide third-party +impls as long as no implementations actually conflict. Perhaps require marking +traits and/or types that permit third-party impls, to ensure that crates can +always implement traits for their own types. + +### The "shiny future" we are working towards + +Long-term, we'll want a way to resolve conflicts between third-party trait +impls. + +We should support a "standalone derive" mechanism, to derive a trait for a type +without attaching the derive to the type definition. We could save a simple +form of type information about a type, and define a standalone deriving +mechanism that consumes exclusively that information. + +Given such a mechanism, we could then permit any crate to invoke the standalone +derive mechanism for a trait and type, and allow identical derivations no +matter where they appear in the dependency tree. + +## Design axioms + +- **Rustaceans should be able to easily integrate a third-party trait with a + third-party type without requiring the cooperation of third-party crate + maintainers.** + +- **It should be possible to *publish* such integration as a new crate.** For + instance, it should be possible to publish an `a_b` crate integrating `a` + with `b`. This makes it easier to scale the ecosystem and get adoption for + new libraries. + +- **Crate authors should have some control over whether their types have + third-party traits implemented.** This ensures that it isn't a breaking + change to introdice first-party trait implementations. + +[da]: ../about/design_axioms.md + +## Ownership and other resources + +**Owner:** TODO + +### Support needed from the project + +* Lang team: + * Design meetings to discuss design changes + * RFC reviews +* Blog post inviting testing, evaluation, and feedback + +## Outputs and milestones + +### Outputs + +The output will be a pair of RFCs: +- A lang RFC proposing a very simple system for binaries to ignore the orphan rule. +- A lang RFC proposing a system with more careful safeguards, to relax the orphan rule for publishable library crates. + +### Milestones + +- Accepted RFCs. + +## Frequently asked questions + +### Won't this create incompatibilities between libraries that implement the same trait for the same type? + +Yes! The orphan rule is a tradeoff. It was established to avert one source of +potential incompatibility between library crates, in order to help the +ecosystem grow, scale, and avoid conflicts. However, the presence of the orphan +rule creates a different set of scaling issues and conflicts. This project goal +proposes to adjust the balance, attempting to achieve some of the benefits of +both. diff --git a/src/2024h2/Seamless-C-Support.md b/src/2024h2/Seamless-C-Support.md new file mode 100644 index 0000000..69147c1 --- /dev/null +++ b/src/2024h2/Seamless-C-Support.md @@ -0,0 +1,137 @@ +# Seamless C support + +| Metadata | | +| --- | --- | +| Owner(s) | *Github usernames or other identifying info for goal owners* | +| Teams | *Names of teams being asked to commit to the goal* | +| Status | WIP | + +## Motivation + +Using C from Rust should be as easy as using C from C++: completely seamless, +as though it's just another module of code. You should be able to drop Rust +code into a C project and start compiling and using it in minutes. + +### The status quo + +Today, people who want to use C and Rust together in a project have to put +substantial work into infrastructure or manual bindings. Whether by creating +build system infrastructure to invoke bindgen/cbindgen (and requiring the +installation of those tools), or manually writing C bindings in Rust, projects +cannot simply drop Rust code into a C program or C code into a Rust program. +This creates a high bar for adopting or experimenting with Rust, and makes it +more difficult to provide Rust bindings for a C library. + +By contrast, dropping C++ code into a C project or C code into a C++ project is +trivial. The same compiler understands both C and C++, and allows compiling +both together or separately. The developer does not need to duplicate +declarations for the two languages, and can freely call between functions in +both languages. + +C and C++ are still not the same language. They have different idioms and +common types, and a C interface may not be the most ergonomic to use from C++. +Using C++ from C involves treating the C as C++, such that it no longer works +with a C compiler that has no C++ support. But nonetheless, C++ and C integrate +extremely well, and C++ is currently the easiest language to integrate into an +established C project. + +This is the level of integration we should aspire to for Rust and C. + +### The next few steps + +To provide seamless integration between Rust and C, we need a single compiler +to understand both Rust and C. Thus, the first step will be to integrate a C +preprocessor and compiler frontend into the Rust compiler. For at least the +initial experimentation, we could integrate components from LLVM, taking +inspiration from `zig cc`. (In the future, we can consider other alternatives, +including a native Rust implementation. We could also consider components from +c2rust or similar.) + +We can either generate MIR directly from C (which would be experimental and +incomplete but integrate better with the compiler), or bypass MIR and generate +LLVM bytecode (which would be simpler but less well integrated). + +This first step would provide substantial benefits already: a C compiler that's +always available on any system with Rust installed, that generates code for any +supported Rust target, and that always supports cross-language optimization. + +We can further improve support for calling C from Rust. We can support +"importing" C header files, to permit using this support to call external +libraries, and to support inline functions. + +### The "shiny future" we are working towards + +Once C support is integrated, we can generate type information for C functions +as if they were unsafe Rust functions, and then support treating the C code as +a Rust module, adding the ability to import and call C functions from Rust. +This would not necessarily even require header files, making it even simpler to +use C from Rust. The initial support can be incomplete, supporting the subset +of C that has reasonable semantics in Rust. + +We will also want to add C features that are missing in Rust, to allow Rust to +call any supported C code. + +Once we have a C compiler integrated into Rust, we can incrementally add C +extensions to support using Rust from C. For instance: +- Support importing Rust modules and calling `extern "C"` functions from + them, without requiring a C header file. +- Support using `::` for scoping names. +- Support simple Rust types (e.g. `Option` and `Result`). +- Support calling Rust methods on objects. +- Allow annotating C functions with Rust-enhanced type signatures, such as + marking them as safe, using Rust references for pointer parameters, or + providing simple lifetime information. + +We can support mixing Rust and C in a source file, to simplify incremental +porting even further. + +To provide simpler integration into C build systems, we can accept a +C-compiler-compatible command line (`CFLAGS`), and apply that to the C code we +process. + +We can also provide a CLI entry point that's sufficiently command-line +compatible to allow using it as `CC` in a C project. + +## Design axioms + +- **C code should feel like just another Rust module.** Integrating C code into + a Rust project, or Rust code into a C project, should be trivial; it should + be just as easy as integrating C with C++. + +- **This is not primarily about providing *safe* bindings.** This project will + primarily make it much easier to access C bindings as unsafe interfaces. + There will still be value in wrapping these unsafe C interfaces with safer + Rust interfaces. + +- **Calling C from Rust should not require writing duplicate information in Rust** + that's already present in a C header or source file. + +- **Integrating C with Rust should not require third-party tools**. + +- **Compiling C code should not require substantially changing the information + normally passed to a C compiler** (e.g. compiler arguments). + +## Ownership and other resources + +**Owner:** TODO + +### Support needed from the project + +* Lang team: + * Design meetings to discuss design changes + * RFC reviews +* Compiler team: + * RFC review + +## Outputs and milestones + +### Outputs + +The initial output will be a pair of RFCs: one for an experimental integration of a C compiler into rustc, and the other for minimal language features to take advantage of that. + +### Milestones + +- Compiler RFC: Integrated C compiler +- Lang RFC: Rust language support for seamless C integration + +## Frequently asked questions diff --git a/src/2024h2/slate.md b/src/2024h2/slate.md index 685eb92..753ad39 100644 --- a/src/2024h2/slate.md +++ b/src/2024h2/slate.md @@ -31,6 +31,8 @@ None | ↳ [Fallible allocation][] | ![Owner needed][own] | | | [Polonius on nightly][] | [lqd] | [Lang], [Types] | | [Impl trait everywhere][] | [oli-obk] | [Lang], [Types] | +| [Seamless C Support][] | ![Owner needed][own] | [Lang] | +| [Relaxing the Orphan Rule][] | ![Owner needed][own] | [Lang] | ## Not accepted goals @@ -48,6 +50,8 @@ None. [Return type notation]: ./Async--AsyncClosures.md [Polonius on nightly]: ./Polonius.md [Impl trait everywhere]: ./Impl-trait-everywhere.md +[Seamless C Support]: ./Seamless-C-Support.md +[Relaxing the Orphan Rule]: ./Relaxing-the-Orphan-Rule.md [own]: https://img.shields.io/badge/Owned%20Needed-blue @@ -60,4 +64,4 @@ None. [LC]: https://www.rust-lang.org/governance/teams/leadership-council [Lang]: https://www.rust-lang.org/governance/teams/lang [Types]: https://www.rust-lang.org/governance/teams/compiler#team-types -[Libs-API]: https://www.rust-lang.org/governance/teams/library#team-libs-api \ No newline at end of file +[Libs-API]: https://www.rust-lang.org/governance/teams/library#team-libs-api