Skip to content

Commit 53ee6ac

Browse files
authored
Merge pull request #1 from ebfull/rfc-rand_core-0.10
rfc: migrate to `rand_core 0.10`
2 parents b46d7af + 0d0755b commit 53ee6ac

1 file changed

Lines changed: 221 additions & 0 deletions

File tree

text/0001-rand-core-0.10.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
- Feature Name: `rand_core_0_10`
2+
- Start Date: 2026-02-22
3+
- RFC PR: [zkcrypto/rfcs#1](https://github.com/zkcrypto/rfcs/pull/1)
4+
- Tracking Issue: [zkcrypto/rfcs#0000](https://github.com/zkcrypto/rfcs/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Upgrade the `ff` and `group` crates (and their downstream crates, e.g. `pairing`, `bls12_381`, and `jubjub`) from `rand_core 0.6` to `rand_core 0.10`, updating all public trait APIs to reflect the renamed and restructured traits in the new `rand_core` release. This is a semver-breaking change requiring new major releases of the affected crates.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
`rand_core 0.10` was released on February 1st, 2026, with significant trait renames and API restructuring, described by its maintainers as the ["last significant breakage before 1.0."](https://github.com/rust-random/rand_core/releases/tag/v0.10.0)
15+
16+
The zkcrypto trait crates (`ff`, `group`) expose `rand_core` types in their public APIs, specifically in `Field::random` and `Group::random`. Because `rand_core` is a public dependency, any upgrade to a new semver-incompatible version of `rand_core` requires a corresponding semver-breaking release of the zkcrypto crates.
17+
18+
There is strong motivation to make this upgrade now.
19+
20+
### Ecosystem alignment
21+
22+
The RustCrypto project (which maintains trait crates like `elliptic-curve` and `digest`) has decided to [skip `rand_core 0.9` entirely](https://github.com/zkcrypto/ff/pull/130#issuecomment-3565178513) and move directly to `rand_core 0.10`. End users that combine zkcrypto and RustCrypto crates in the same dependency tree would otherwise be forced to maintain incompatible duplicates of the `rand_core` traits.
23+
24+
### Avoiding double disruption
25+
26+
The zkcrypto crates currently depend on `rand_core 0.6`. We originally planned an intermediate upgrade to `rand_core 0.9`, but since the broader ecosystem (including RustCrypto) is skipping 0.9, performing a single jump from 0.6 to 0.10 is less disruptive to downstream users than two sequential breaking upgrades.
27+
28+
### Downstream demand
29+
30+
Multiple contributors and downstream projects have requested this upgrade[^demand], and prototype work has already been done on the `release-0.14.0` branches of `ff` and `group`.
31+
32+
### MSRV is reasonable
33+
34+
Rust 1.85 (the Rust 2024 edition boundary) was released over a year ago, and is already the MSRV for both the RustCrypto crates and popular `ff` consumers like [`librustzcash`](https://github.com/zcash/librustzcash).
35+
36+
# Guide-level explanation
37+
[guide-level-explanation]: #guide-level-explanation
38+
39+
## MSRV bump
40+
41+
The minimum supported Rust version for all affected crates is raised to **1.85.0**. This is required by `rand_core 0.10`.
42+
43+
## `Field::random` and `Group::random`
44+
45+
The `random` method signature changes from taking ownership of an RNG to taking a mutable reference:
46+
47+
```rust
48+
// BEFORE:
49+
fn random(rng: impl RngCore) -> Self;
50+
51+
// AFTER:
52+
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self;
53+
```
54+
55+
The bound changes from `RngCore` to `Rng` (which is the new name for the same concept in `rand_core 0.10`). The method now also takes `&mut R` instead of `impl RngCore`, and relaxes the `Sized` requirement on the RNG.
56+
57+
**Migration:** Most call sites that pass `&mut rng` will continue to work. Call sites that pass an RNG by value (e.g. `Field::random(OsRng)`) will need to change to pass by reference (e.g. `Field::random(&mut OsRng)`). Since `OsRng` has been removed from `rand_core`, users should migrate to `getrandom::SysRng` (from the [`getrandom`](https://crates.io/crates/getrandom) crate) or equivalent.
58+
59+
## `Field::try_random` and `Group::try_random` (new)
60+
61+
A new fallible method is introduced for random element generation:
62+
63+
```rust
64+
fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error>;
65+
```
66+
67+
This is now the *required* method of the trait; `random` becomes a *provided* method that delegates to `try_random` and unwraps the infallible result. This enables use of fallible RNG sources (e.g. hardware RNGs that can fail) without panicking.
68+
69+
**For trait implementors:** You now implement `try_random` instead of `random`. The `random` method has a default implementation that calls `try_random` and handles the `Infallible` error case. Note that the bound on `try_random` uses `TryRng` (renamed from `TryRngCore` in `rand_core 0.10`). Inside your implementation, RNG method calls also change to their fallible counterparts (e.g. `rng.next_u64()` becomes `rng.try_next_u64()?`).
70+
71+
# Reference-level explanation
72+
[reference-level-explanation]: #reference-level-explanation
73+
74+
## `ff` crate changes
75+
76+
### Dependency changes
77+
78+
| Dependency | Before | After |
79+
|---|---|---|
80+
| `rand_core` | `0.6` | `0.10` |
81+
| MSRV | `1.56` | `1.85` |
82+
83+
### `Field` trait changes
84+
85+
The `Field` trait's RNG-related methods change as follows:
86+
87+
```rust
88+
// BEFORE:
89+
use rand_core::RngCore;
90+
91+
pub trait Field: /* ... */ {
92+
fn random(rng: impl RngCore) -> Self;
93+
// ...
94+
}
95+
96+
// AFTER:
97+
use rand_core::{Rng, TryRng};
98+
99+
pub trait Field: /* ... */ {
100+
/// Provided method (infallible RNG).
101+
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
102+
// `Rng: TryRng<Error = Infallible>`, so this is exhaustive.
103+
let Ok(out) = Self::try_random(rng);
104+
out
105+
}
106+
107+
/// Required method (fallible RNG).
108+
fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error>;
109+
// ...
110+
}
111+
```
112+
113+
Key details:
114+
- `random` changes from a **required** method to a **provided** method.
115+
- `try_random` is the new **required** method that trait implementors must provide.
116+
- The `?Sized` bound on `R` allows passing trait objects (e.g., `&mut dyn Rng`), which was not possible before.
117+
- `Rng` is an extension trait of `TryRng<Error = Infallible>`, so any infallible RNG satisfies the `TryRng` bound used by `try_random`.
118+
119+
## `group` crate changes
120+
121+
### Dependency changes
122+
123+
| Dependency | Before | After |
124+
|---|---|---|
125+
| `ff` | `0.13` | next major |
126+
| `rand_core` | `0.6` | `0.10` |
127+
| MSRV | `1.56` | `1.85` |
128+
129+
### `Group` trait changes
130+
131+
Mirrors the `Field` trait changes:
132+
133+
```rust
134+
// BEFORE:
135+
pub trait Group: /* ... */ {
136+
fn random(rng: impl RngCore) -> Self;
137+
// ...
138+
}
139+
140+
// AFTER:
141+
pub trait Group: /* ... */ {
142+
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self { /* default impl */ }
143+
fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error>;
144+
// ...
145+
}
146+
```
147+
148+
## Downstream crate impact
149+
150+
Downstream crates that implement `Field` or `Group` will need mechanical updates: each `random` implementation becomes a `try_random` implementation, and call sites are updated to use the new signatures. Crates that transitively depend on `ff` or `group` without directly using `rand_core` types (e.g. `pairing`) only need a version bump. The zkcrypto-maintained downstream crates (`bls12_381`, `jubjub`, `pairing`) will receive corresponding updates as part of this effort.[^bls12-prototype]
151+
152+
## Trait rename mapping (`rand_core 0.6``rand_core 0.10`)
153+
154+
For reference, the full trait rename mapping relevant to zkcrypto:
155+
156+
| `rand_core 0.6` | `rand_core 0.10` |
157+
|---|---|
158+
| `RngCore` | `Rng` |
159+
| N/A | `TryRng` |
160+
| `CryptoRng` | `CryptoRng` |
161+
| N/A | `TryCryptoRng` |
162+
163+
Note: The `release-0.14.0` branches originally targeted `rand_core 0.9`; this RFC proposes targeting `rand_core 0.10` with the updated trait names.
164+
165+
# Drawbacks
166+
[drawbacks]: #drawbacks
167+
168+
- **MSRV jump from 1.56 to 1.85.** This is a significant MSRV increase (29 versions). However, `rand_core 0.10` hard-requires 1.85 (it uses the Rust 2024 edition), so there is no way to avoid this bump while upgrading. In practice, the primary downstream consumers (Zcash ecosystem, RustCrypto) already require 1.85.
169+
170+
- **Breaking change for all downstream crates.** Every crate that implements `Field` or `Group` will need to update their implementations. However, the migration is mechanical (rename `random` to `try_random`, update the signature), and the old `random` method continues to exist as a provided method with the same semantics.
171+
172+
# Rationale and alternatives
173+
[rationale-and-alternatives]: #rationale-and-alternatives
174+
175+
## Why skip `rand_core 0.9`?
176+
177+
We originally planned to upgrade to `rand_core 0.9` (see the `release-0.14.0` branches). However:
178+
179+
- RustCrypto decided to [skip `rand_core 0.9` entirely](https://github.com/zkcrypto/ff/pull/130#issuecomment-3565178513) and move to `rand_core 0.10`.
180+
- Releasing `ff` with `rand_core 0.9` would be ["nigh-unusable"](https://github.com/zkcrypto/ff/pull/130#issuecomment-3565178513) for downstream crates that also depend on RustCrypto trait crates, since those would require `rand_core 0.10`.
181+
- Performing a single breaking change (0.6 → 0.10) causes less ecosystem churn than two sequential breaking changes (0.6 → 0.9 → 0.10).
182+
183+
## Why make `try_random` the required method instead of `random`?
184+
185+
The `rand_core 0.9`/`0.10` design philosophy makes fallibility the primary interface, with infallible usage as a convenience wrapper. By making `try_random` the required method:
186+
187+
- Implementors only need to write one implementation that works for both fallible and infallible RNGs.
188+
- The `random` method "just works" via the default implementation.
189+
- This aligns with the direction of `rand_core` itself and addresses [ff#109](https://github.com/zkcrypto/ff/issues/109).
190+
191+
## Why not wait for `rand_core` 1.0?
192+
193+
`rand_core 0.10` is explicitly described as the "last significant breakage before 1.0." Waiting for 1.0 would leave the zkcrypto ecosystem stuck on `rand_core 0.6` indefinitely while the rest of the Rust ecosystem moves forward. Additionally, the transition from 0.10 to 1.0 should be minimally disruptive.
194+
195+
## What happens if we don't do this?
196+
197+
Not upgrading would leave the zkcrypto ecosystem unable to interoperate with the post-`rand_core 0.10` versions of RustCrypto crates, effectively forking the Rust cryptography ecosystem into incompatible dependency trees. This would be particularly problematic for downstream crates (e.g. `bls12_381`) that bridge both ecosystems.
198+
199+
# Prior art
200+
[prior-art]: #prior-art
201+
202+
- **RustCrypto ecosystem.** The RustCrypto project is performing the same `rand_core 0.10` migration across all of their trait and implementation crates. Their decision to skip `rand_core 0.9` directly informed our approach. See [RustCrypto/meta#19](https://github.com/RustCrypto/meta/issues/19) for their discussion of RNG API conventions, including fallible RNG interfaces.
203+
204+
- **`rand_core` 0.10 release.** The [release notes](https://github.com/rust-random/rand_core/releases/tag/v0.10.0) document the rationale for the trait renames (`RngCore``Rng`, `TryRngCore``TryRng`) and the removal of OS RNG functionality to the `getrandom` crate.
205+
206+
- **`getrandom` 0.4.** The `getrandom` crate has taken over the OS-level RNG functionality previously in `rand_core` (specifically `OsRng`), now exposed as `getrandom::SysRng` (introduced in `getrandom 0.4`).
207+
208+
# Unresolved questions
209+
[unresolved-questions]: #unresolved-questions
210+
211+
- **Release version numbers for `ff` and `group`.** Both crates are expected to release as version **0.14**, but the exact version numbers will be confirmed during implementation.
212+
213+
# Future possibilities
214+
[future-possibilities]: #future-possibilities
215+
216+
- **Removing the `random` convenience method.** In the long term, it may make sense to deprecate or remove `Field::random` / `Group::random` in favor of exclusively using `try_random`, aligning fully with the `rand_core` design philosophy. However, this is not proposed in this RFC.
217+
218+
- **Edition 2024 migration.** With MSRV 1.85, all crates could migrate to `edition = "2024"` in a follow-up release.
219+
220+
[^demand]: [ff#109](https://github.com/zkcrypto/ff/issues/109), [ff#122](https://github.com/zkcrypto/ff/pull/122), [ff#147](https://github.com/zkcrypto/ff/pull/147), [group#56](https://github.com/zkcrypto/group/pull/56)
221+
[^bls12-prototype]: A prototype migration PR for `bls12_381` already exists: [bls12_381#145](https://github.com/zkcrypto/bls12_381/pull/145).

0 commit comments

Comments
 (0)