| # Unstable Features |
| |
| ## Unstable features used by Crubit {#accepted} |
| |
| For each feature we use, we document the following: |
| |
| * **Crubit feature:** Under which Crubit feature flags do we use this? If it |
| is under `supported`, then the Rust feature breaking can break Crubit users |
| in general. If it is under `experimental`, then it only breaks tests and |
| close partner teams. |
| * **Use case:** What do we use the feature for? |
| * **Exit strategy:** What would happen if the feature went away? |
| |
| ### `custom_inner_attributes` |
| |
| * **Crubit feature:** `supported` |
| * **Use case:** Used to suppress automatic formatting, to make our golden |
| tests stable even in the face of e.g. non-idempotency bugs in `rustfmt`. |
| * **Exit strategy:** Disable `rustfmt` on the golden tests some other way. |
| |
| ### `negative_impls` |
| |
| * **Crubit feature:** `supported` |
| * **Use case:** Used to implement `ctor` / nontrivial intialization, so that |
| we can dispatch on the existence of the `Unpin` trait (which is not possible |
| with `PhantomPinned`). Also used so that we can pin/unpin a type without |
| adding a field (which is not possible with `PhantomPinned`). |
| * **Exit strategy:** For dispatch, we can define a new auto trait (also an |
| unstable feature) or use specialization (also an unstable feature). For the |
| fields, this is a compatibility break, and we'd need to add a PhantomData |
| field to all C++ types to mark them as `!Send`, `!Unpin`, etc. |
| |
| ### `vec_into_raw_parts` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used for conversions of vectors of forward-declared |
| objects, which is not yet released. |
| * **Exit strategy:** We could delete this if we had to. Hopefully low-risk, |
| people love raw parts. |
| |
| ### `extern_types` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used for forward declarations, which have an unknown size. |
| * **Exit strategy:** Hard to avoid: without this, we get UB for forward |
| declarations if you use references, due to provenance rules. Don't need the |
| `forward_declare` crate except for migration from existing C++ code to Rust, |
| where it relies on forward declarations for build performance or |
| cycle-breaking reasons. |
| |
| ### `arbitrary_self_types` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** We need this or an equivalent feature in order to make methods |
| callable on rvalue references but not regular const references, so as to |
| support C++ move semantics. Also likely need it for aliasing-safe |
| references. |
| * **Exit strategy:** Likely can't live without this feature. Lowish risk, in |
| that Rust needs user-defined types to be usable as self types (e.g. it |
| already has this for Rc), although the exact mechanism is as-yet undecided. |
| |
| ### `never_type` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used in a test to demonstrate we don't support it. |
| * **Exit strategy:** Delete test. |
| |
| ### `c_variadic` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used in a test to demonstrate we don't support it. |
| * **Exit strategy:** Delete test. |
| |
| ### `abi_vectorcall` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used in a test to test non-C calling conventions. |
| * **Exit strategy:** Delete test at worst, replace with different non-C |
| calling convention at best. |
| |
| ### `impl_trait_in_assoc_type` |
| |
| * **Crubit feature:** `experimental` |
| * **Use case:** Used for returning `impl Ctor` for nontrivial construction. |
| * **Exit strategy:** Replace with |
| [RPITIT](https://github.com/rust-lang/rfcs/pull/3425), which is stable now. |
| |
| ### `allocator_api` |
| |
| * **Crubit feature:** unreleased |
| * **Use case:** Used for an ABI-compatible implementation of `std::vector` |
| that can reuse the C++ allocator. |
| * **Exit strategy:** Do not release, or release but require that the Rust |
| global allocator *is* the C++ allocator. |
| |
| ### `cfg_sanitize` |
| |
| * **Crubit feature:** unreleased |
| * **Use case:** Used for an ABI-compatible implementation of `std::vector` |
| that poisons memory in the same way as libc++. |
| * **Exit strategy:** Do not release, or release but with degraded |
| AddressSanitizer results that hide bugs. |
| |
| ### `cfg_accessible`, `stmt_expr_attributes`, `proc_macro_hygiene` |
| |
| * **Crubit feature:** N/A, used internally by Crubit |
| * **Use case:** Crubit uses rustc as a library, which is subject to |
| backwards-incompatible changes. `cfg_accessible` makes it much easier to |
| write code which is compatible with two versions of rustc at the same time. |
| `stmt_expr_attributes`, `proc_macro_hygiene` lets us handle |
| backwards-incompatible changes affecting individual expressions. |
| * **Exit strategy:** If `cfg_accessible` changes during the very same update |
| we are trying to be compatible with, we can't use it in the first place. If |
| it changes in a future version, then the update has already completed |
| successfully, so we can delete the usage (keeping the `cfg_accessible` usage |
| that is currently used, and deleting the other one). Same for the rest. |
| |
| ### `unsized_const_params` |
| |
| * **Crubit feature:** `supported` |
| * **Use case:** Better error messages when you get a type error on forward |
| declarations. `Symbol<(C<'F'>, C<'o'>, C<'o'>)>` is so substantially worse |
| than `Symbol<"Foo">` that Rust will even hide the type entirely, forcing you |
| to read the name from a separate file (which may not be available to you if |
| you were doing a remote build). |
| * **Exit strategy:** The `Symbol<(...)>` approach works and is tested, and we |
| can switch back with a single flag flip. The only impact is a regression on |
| error messages. |
| |
| ## Unstable features **not** used by Crubit {#rejected} |
| |
| The following features are ones we'd hypothetically like to use, but do not. |
| |
| ### `try_trait_v2` |
| |
| Crubit does aim to support the Abseil error type, `absl::Status`, which ideally |
| would be usable everywhere a `Result` is for ergonomics. For example, we'd |
| expect you to be able to write code like the following: |
| |
| ```rust |
| pub fn read() -> Status { |
| foo()?; |
| } |
| ``` |
| |
| Where `foo` returns a `Status`, or even an `io::Result`. |
| |
| For this to work with `Status` being *exactly the same type* as it is in C++, |
| with the same layout, we need to use `try_trait_v2`. |
| |
| Until it is stabilized, or the cost/benefit becomes worthwhile, we can work |
| around it by using `Result`, and converting when a `Result` is passed or |
| returned by value. |
| |
| ### `fn_traits` |
| |
| Much like `try_traits_v2`, we'd like to support C++ "function objects" which |
| implement the call operator. In Rust, without `fn_traits`, the API is |
| awkward: given a `foo` of type `std::function`, `absl::AnyInvocable`, or |
| `absl::FunctionRef`, you cannot just write `foo()` to call it, because it cannot |
| implement the `Fn*` family of traits. This makes these types less convenient to |
| use than they are in C++, requiring something like `foo.as_closure()()` to |
| create a closure out of a `FunctionRef` or similar. |
| |
| (Another use case we've examined in the past: in addition to implementing |
| function objects, `fn_traits` could also be used to implement overloading, |
| including overloaded function objects. For example, an overloaded function |
| could, instead, be a constant with many different implementations of `Fn`, for |
| different parameter types. Though, this ends up looking a bit |
| odd: `(mystruct.overloaded)()`) |
| |
| ### `min_specialization` |
| |
| We have many use cases for `min_specialization`, including bindings for C++ |
| templates that have explicit template specialization or partial template |
| specialization. However, they are not yet pressing, and specialization related |
| Rust features are especially risky. |
| |
| ### `inherent_associated_types` |
| |
| Inherent associated types give a natural Rust spelling of the following C++ |
| type definition: |
| |
| ```c++ |
| struct Foo { |
| using MyAlias = int; |
| }; |
| ``` |
| |
| ### `disjoint_associated_types` |
| |
| [`disjoint_associated_types`](https://github.com/rust-lang/rfcs/pull/1672) was |
| closed, but not necessarily permanently. See also |
| https://github.com/rust-lang/rust/issues/20400. |
| |
| Not every C++ type supports the Rust move operation. For more on this, see |
| [Classes and Structs](../cpp/classes_and_structs#trivially_relocatable). |
| |
| Crubit (in `experimental` mode) supports passing and returning these non-Rust-movable C++ |
| objects by value. But since they are not Rust-movable, they cannot literally |
| be returned in Rust by value: `pub fn foo() -> X` performs a Rust move of its |
| return type by definition. |
| |
| Instead, these objects support lazy construction, in the same style as |
| [`moveit`](https://mcyoung.xyz/2021/04/26/move-ctors/). See |
| [`ctor.rs`](https://github.com/google/crubit/blob/main/support/ctor.rs). This |
| results in, for example, the following API differences: |
| |
| `X` is rust-movable | `X` is not rust-movable |
| -------------------- | --------------------------------------- |
| `pub fn foo() -> X` | `pub fn foo() -> impl Ctor<Output=X>` |
| `impl Add<X> for &C` | `impl<T: Ctor<Output=X>> Add<T> for &C` |
| |
| The problem comes in with operator overloading: the following is valid: |
| |
| ```rust |
| impl Add<X> for &C {...} |
| impl Add<Y> for &C {...} |
| ``` |
| But the equivalent using this lazy-construction trait is not: |
| |
| ```rust |
| impl<T: Ctor<Output=X>> Add<T> for &C {...} |
| impl<T: Ctor<Output=Y>> Add<T> for &C {...} |
| ``` |
| |
| Rust doesn't know that these two are disjoint, meaning that we cannot use the |
| `Ctor` trait approach for traits. |
| |
| An alternative fix would be language support for in-place pinned construction. |
| That would render the `Ctor` trait obsolete, and reduce Crubit's needs around |
| trait coherence (as well as `negative_impls`, above). |
| |
| |
| ### `register_tool` |
| |
| We previously used this attribute to allow Crubit to read annotations on |
| types/functions. However, the need for end-users to enable a feature gate and |
| re-declare the tool caused us to migrate away to using `#[doc]` attributes. |
| |
| We use custom attributes so that Crubit can round-trip a type correctly, or to |
| implement automated bridging so that a C++ `Status` becomes a Rust `Result<(), |
| StatusError>`, or what have you. |
| |
| ### `super_let` |
| |
| Crubit's `ctor.rs` crate uses a syntax like `emplace!{let x = ...}` to in-place |
| initialize a pinned value using a C++ constructor. It cannot use the same trick |
| that `pin!()` does, because it needs to actually call a function to initialize |
| the memory, and so lifetime extension would not apply: the memory, being passed |
| to a function, would have its lifetime end at the end of the statement. |
| |
| `super_let` would allow us to instead write `let x = emplace!(...)`, with the |
| macro expanding to a use of `super` to lifetime-extend the storage |
| subexpression. |
| |
| For example: |
| |
| ```rust |
| macro_rules! emplace { |
| ($expr:expr) => { |
| super($crate::Slot::unsafe_new()) |
| .unsafe_construct($expr) |
| .unsafe_as_pin_unchecked() |
| }; |
| } |
| ``` |
| |
| Until `super_let` is stabilized, the outer `emplace!{}` works OK, it's just |
| unexpected and causes code formatting problems. |