Skip to content

Commit 6078b98

Browse files
authored
Merge pull request #3 from joshtriplett/wip-goals
Add two draft project goals: seamless C support, and relaxing the orphan rule
2 parents 473b6f4 + d3d8d44 commit 6078b98

File tree

3 files changed

+274
-1
lines changed

3 files changed

+274
-1
lines changed
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Relaxing the Orphan Rule
2+
3+
| Metadata | |
4+
| --- | --- |
5+
| Owner(s) | |
6+
| Teams | *lang* |
7+
| Status | WIP |
8+
9+
## Motivation
10+
11+
Relax the orphan rule, in limited circumstances, to allow crates to provide
12+
implementations of third-party traits for third-party types. The orphan rule
13+
averts one potential source of conflicts between Rust crates, but its presence
14+
also creates scaling issues in the Rust community: it prevents providing a
15+
third-party library that integrates two other libraries with each other, and
16+
instead requires convincing the author of one of the two libraries to add
17+
(optional) support for the other, or requires using a newtype wrapper. Relaxing
18+
the orphan rule, carefully, would make it easier to integrate libraries with
19+
each other, share those integrations, and make it easier for new libraries to
20+
garner support from the ecosystem.
21+
22+
### The status quo
23+
24+
Suppose a Rust developer wants to work with two libraries: `lib_a` providing
25+
trait `TraitA`, and `lib_b` providing type `TypeB`. Due to the orphan rule, if
26+
they want to use the two together, they have the following options:
27+
28+
- Convince the maintainer of `lib_a` to provide `impl TraitA for TypeB`. This
29+
typically involves an optional dependency on `lib_b`. This usually only
30+
occurs if `lib_a` is substantially less popular than `lib_b`, or the
31+
maintainer of `lib_a` is convinced that others are likely to want to use the
32+
two together. This tends to feel "reversed" from the norm.
33+
34+
- Convince the maintainer of `lib_b` to provide `impl TraitA for TypeB`. This
35+
typically involves an optional dependency on `lib_a`. This is only likely to
36+
occur if `lib_a` is popular, and the maintainer of `lib_b` is convinced that
37+
others may want to use the two together. The difficulty in advocating this,
38+
scaled across the community, is one big reason why it's difficult to build
39+
new popular crates built around traits (e.g. competing
40+
serialization/deserialization libraries, or competing async I/O traits).
41+
42+
- Vendor either `lib_a` or `lib_b` into their own project. This is
43+
inconvenient, adds maintenance costs, and isn't typically an option for
44+
public projects intended for others to use.
45+
46+
- Create a newtype wrapper around `TypeB`, and implement `TraitA` for the
47+
wrapper type. This is less convenient, propagates throughout the crate (and
48+
through other crates if doing this in a library), and may require additional
49+
trait implementations for the wrapper that `TypeB` already implemented.
50+
51+
All of these solutions are suboptimal in some way, and inconvenient. In
52+
particular, all of them are much more difficult than actually writing the trait
53+
impl. All of them tend to take longer, as well, slowing down whatever goal
54+
depended on having the trait impl.
55+
56+
### The next few steps
57+
58+
As an initial experiment, try relaxing the orphan rule for binary crates, since
59+
this cannot create library incompatibilities in the ecosystem. Allow binary
60+
crates to implement third-party traits for third-party types, possibly
61+
requiring a marker on either the trait or type or both. See how well this works
62+
for users.
63+
64+
As a second experiment, try allowing library crates to provide third-party
65+
impls as long as no implementations actually conflict. Perhaps require marking
66+
traits and/or types that permit third-party impls, to ensure that crates can
67+
always implement traits for their own types.
68+
69+
### The "shiny future" we are working towards
70+
71+
Long-term, we'll want a way to resolve conflicts between third-party trait
72+
impls.
73+
74+
We should support a "standalone derive" mechanism, to derive a trait for a type
75+
without attaching the derive to the type definition. We could save a simple
76+
form of type information about a type, and define a standalone deriving
77+
mechanism that consumes exclusively that information.
78+
79+
Given such a mechanism, we could then permit any crate to invoke the standalone
80+
derive mechanism for a trait and type, and allow identical derivations no
81+
matter where they appear in the dependency tree.
82+
83+
## Design axioms
84+
85+
- **Rustaceans should be able to easily integrate a third-party trait with a
86+
third-party type without requiring the cooperation of third-party crate
87+
maintainers.**
88+
89+
- **It should be possible to *publish* such integration as a new crate.** For
90+
instance, it should be possible to publish an `a_b` crate integrating `a`
91+
with `b`. This makes it easier to scale the ecosystem and get adoption for
92+
new libraries.
93+
94+
- **Crate authors should have some control over whether their types have
95+
third-party traits implemented.** This ensures that it isn't a breaking
96+
change to introdice first-party trait implementations.
97+
98+
[da]: ../about/design_axioms.md
99+
100+
## Ownership and other resources
101+
102+
**Owner:** TODO
103+
104+
### Support needed from the project
105+
106+
* Lang team:
107+
* Design meetings to discuss design changes
108+
* RFC reviews
109+
* Blog post inviting testing, evaluation, and feedback
110+
111+
## Outputs and milestones
112+
113+
### Outputs
114+
115+
The output will be a pair of RFCs:
116+
- A lang RFC proposing a very simple system for binaries to ignore the orphan rule.
117+
- A lang RFC proposing a system with more careful safeguards, to relax the orphan rule for publishable library crates.
118+
119+
### Milestones
120+
121+
- Accepted RFCs.
122+
123+
## Frequently asked questions
124+
125+
### Won't this create incompatibilities between libraries that implement the same trait for the same type?
126+
127+
Yes! The orphan rule is a tradeoff. It was established to avert one source of
128+
potential incompatibility between library crates, in order to help the
129+
ecosystem grow, scale, and avoid conflicts. However, the presence of the orphan
130+
rule creates a different set of scaling issues and conflicts. This project goal
131+
proposes to adjust the balance, attempting to achieve some of the benefits of
132+
both.

src/2024h2/Seamless-C-Support.md

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Seamless C support
2+
3+
| Metadata | |
4+
| --- | --- |
5+
| Owner(s) | *Github usernames or other identifying info for goal owners* |
6+
| Teams | *Names of teams being asked to commit to the goal* |
7+
| Status | WIP |
8+
9+
## Motivation
10+
11+
Using C from Rust should be as easy as using C from C++: completely seamless,
12+
as though it's just another module of code. You should be able to drop Rust
13+
code into a C project and start compiling and using it in minutes.
14+
15+
### The status quo
16+
17+
Today, people who want to use C and Rust together in a project have to put
18+
substantial work into infrastructure or manual bindings. Whether by creating
19+
build system infrastructure to invoke bindgen/cbindgen (and requiring the
20+
installation of those tools), or manually writing C bindings in Rust, projects
21+
cannot simply drop Rust code into a C program or C code into a Rust program.
22+
This creates a high bar for adopting or experimenting with Rust, and makes it
23+
more difficult to provide Rust bindings for a C library.
24+
25+
By contrast, dropping C++ code into a C project or C code into a C++ project is
26+
trivial. The same compiler understands both C and C++, and allows compiling
27+
both together or separately. The developer does not need to duplicate
28+
declarations for the two languages, and can freely call between functions in
29+
both languages.
30+
31+
C and C++ are still not the same language. They have different idioms and
32+
common types, and a C interface may not be the most ergonomic to use from C++.
33+
Using C++ from C involves treating the C as C++, such that it no longer works
34+
with a C compiler that has no C++ support. But nonetheless, C++ and C integrate
35+
extremely well, and C++ is currently the easiest language to integrate into an
36+
established C project.
37+
38+
This is the level of integration we should aspire to for Rust and C.
39+
40+
### The next few steps
41+
42+
To provide seamless integration between Rust and C, we need a single compiler
43+
to understand both Rust and C. Thus, the first step will be to integrate a C
44+
preprocessor and compiler frontend into the Rust compiler. For at least the
45+
initial experimentation, we could integrate components from LLVM, taking
46+
inspiration from `zig cc`. (In the future, we can consider other alternatives,
47+
including a native Rust implementation. We could also consider components from
48+
c2rust or similar.)
49+
50+
We can either generate MIR directly from C (which would be experimental and
51+
incomplete but integrate better with the compiler), or bypass MIR and generate
52+
LLVM bytecode (which would be simpler but less well integrated).
53+
54+
This first step would provide substantial benefits already: a C compiler that's
55+
always available on any system with Rust installed, that generates code for any
56+
supported Rust target, and that always supports cross-language optimization.
57+
58+
We can further improve support for calling C from Rust. We can support
59+
"importing" C header files, to permit using this support to call external
60+
libraries, and to support inline functions.
61+
62+
### The "shiny future" we are working towards
63+
64+
Once C support is integrated, we can generate type information for C functions
65+
as if they were unsafe Rust functions, and then support treating the C code as
66+
a Rust module, adding the ability to import and call C functions from Rust.
67+
This would not necessarily even require header files, making it even simpler to
68+
use C from Rust. The initial support can be incomplete, supporting the subset
69+
of C that has reasonable semantics in Rust.
70+
71+
We will also want to add C features that are missing in Rust, to allow Rust to
72+
call any supported C code.
73+
74+
Once we have a C compiler integrated into Rust, we can incrementally add C
75+
extensions to support using Rust from C. For instance:
76+
- Support importing Rust modules and calling `extern "C"` functions from
77+
them, without requiring a C header file.
78+
- Support using `::` for scoping names.
79+
- Support simple Rust types (e.g. `Option` and `Result`).
80+
- Support calling Rust methods on objects.
81+
- Allow annotating C functions with Rust-enhanced type signatures, such as
82+
marking them as safe, using Rust references for pointer parameters, or
83+
providing simple lifetime information.
84+
85+
We can support mixing Rust and C in a source file, to simplify incremental
86+
porting even further.
87+
88+
To provide simpler integration into C build systems, we can accept a
89+
C-compiler-compatible command line (`CFLAGS`), and apply that to the C code we
90+
process.
91+
92+
We can also provide a CLI entry point that's sufficiently command-line
93+
compatible to allow using it as `CC` in a C project.
94+
95+
## Design axioms
96+
97+
- **C code should feel like just another Rust module.** Integrating C code into
98+
a Rust project, or Rust code into a C project, should be trivial; it should
99+
be just as easy as integrating C with C++.
100+
101+
- **This is not primarily about providing *safe* bindings.** This project will
102+
primarily make it much easier to access C bindings as unsafe interfaces.
103+
There will still be value in wrapping these unsafe C interfaces with safer
104+
Rust interfaces.
105+
106+
- **Calling C from Rust should not require writing duplicate information in Rust**
107+
that's already present in a C header or source file.
108+
109+
- **Integrating C with Rust should not require third-party tools**.
110+
111+
- **Compiling C code should not require substantially changing the information
112+
normally passed to a C compiler** (e.g. compiler arguments).
113+
114+
## Ownership and other resources
115+
116+
**Owner:** TODO
117+
118+
### Support needed from the project
119+
120+
* Lang team:
121+
* Design meetings to discuss design changes
122+
* RFC reviews
123+
* Compiler team:
124+
* RFC review
125+
126+
## Outputs and milestones
127+
128+
### Outputs
129+
130+
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.
131+
132+
### Milestones
133+
134+
- Compiler RFC: Integrated C compiler
135+
- Lang RFC: Rust language support for seamless C integration
136+
137+
## Frequently asked questions

src/2024h2/slate.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ None
3131
|[Fallible allocation][] | ![Owner needed][own] | |
3232
| [Polonius on nightly][] | [lqd] | [Lang], [Types] |
3333
| [Impl trait everywhere][] | [oli-obk] | [Lang], [Types] |
34+
| [Seamless C Support][] | ![Owner needed][own] | [Lang] |
35+
| [Relaxing the Orphan Rule][] | ![Owner needed][own] | [Lang] |
3436

3537
## Not accepted goals
3638

@@ -47,6 +49,8 @@ None.
4749
[Fallible allocation]: ./Fallible-allocation.md
4850
[Polonius on nightly]: ./Polonius.md
4951
[Impl trait everywhere]: ./Impl-trait-everywhere.md
52+
[Seamless C Support]: ./Seamless-C-Support.md
53+
[Relaxing the Orphan Rule]: ./Relaxing-the-Orphan-Rule.md
5054

5155
[own]: https://img.shields.io/badge/Owned%20Needed-blue
5256

@@ -59,4 +63,4 @@ None.
5963
[LC]: https://www.rust-lang.org/governance/teams/leadership-council
6064
[Lang]: https://www.rust-lang.org/governance/teams/lang
6165
[Types]: https://www.rust-lang.org/governance/teams/compiler#team-types
62-
[Libs-API]: https://www.rust-lang.org/governance/teams/library#team-libs-api
66+
[Libs-API]: https://www.rust-lang.org/governance/teams/library#team-libs-api

0 commit comments

Comments
 (0)