Skip to content

[refactor][important] bump rand_distr to 0.5.1 #365

@Mec-iS

Description

@Mec-iS

Clarifying the Target: rand_distr 0.5.1rand 0.9.x

Targeting the current ecosystem pairing: rand = "0.9" + rand_distr = "0.5.1" (released January 2025). These two crates form a coupled release: rand_distr 0.5 requires rand 0.9 and they must move together. smartcore currently sits on rand = "0.8.5" + rand_distr = "0.4" + getrandom = "0.2.8".[^1][^2][^3]


The Three-Crate Alignment Problem

This is the core challenge. rand 0.9 internally requires getrandom 0.3. Since smartcore directly depends on getrandom for its WASM path, the three crates must all be bumped atomically — you cannot upgrade rand without also upgrading getrandom, or the dependency resolver will pull conflicting versions into the tree:[^4]

Crate Current Target Coupled constraint
rand 0.8.5 0.9.x requires getrandom 0.3 internally
rand_distr 0.4 0.5.1 requires rand 0.9
getrandom (wasm32) 0.2.8 0.3.x feature rename: jswasm_js [^5]

Flag-by-Flag Breakdown

Smartcore has three distinct random code paths, each with different exposure to breaking changes:[^1]

Flag 1: Default (no extra flags) — SmallRng path

Uses SmallRng from rand with a fixed fallback seed of 0 on no-std / non-wasm targets. Risk: Low. SmallRng still exists in rand 0.9, and the small_rng feature is now enabled by default in rand 0.9. Since smartcore sets default-features = false, the feature must remain explicit — but the name is unchanged.[^6]

# No change needed beyond the version bump
rand = { version = "0.9", default-features = false, features = ["small_rng"] }

Flag 2: stdrandStdRng + thread_rng() path

This flag activates StdRng and seeds it via rand::thread_rng().next_u64(). There are two changes here:[^6]

  • thread_rng()renamed to rng() (old name deprecated, not removed yet)
  • In rand 0.9 with default-features = false, the thread_rng feature is now a separate explicit feature that must be pulled in[^6]

src/rand_custom.rs surgical change:

// Before (rand 0.8)
RngImpl::seed_from_u64(rand::thread_rng().next_u64())

// After (rand 0.9)
RngImpl::seed_from_u64(rand::rng().next_u64())

Cargo.toml stdrand feature update:

# Before
stdrand = ["rand/std_rng", "rand/std"]

# After — thread_rng is now its own feature in 0.9
stdrand = ["rand/std_rng", "rand/std", "rand/thread_rng"]

Flag 3: js — WebAssembly in-browser path

This is the most critical and breaking change in the entire upgrade. getrandom 0.3 removed the js crate feature entirely and replaced it with wasm_js. The current smartcore feature definition:[^5][^7]

# Current (getrandom 0.2.8)
js = ["getrandom/js"]

# Required (getrandom 0.3)
js = ["getrandom/wasm_js"]

This is a public API surface change — any downstream user who enables the js feature in their own Cargo.toml to target wasm32-unknown-unknown will get a compile error at the getrandom/js feature reference if smartcore is partially migrated. The rename must be complete and documented in release notes.[^8][^9]


Additional API-Level Changes

Beyond the random harness flags themselves, rand 0.9 introduces deprecation-level renames that won't break compilation immediately but will generate warnings and will need cleanup:[^6]

gen_rangerandom_range — used in src/ensemble/base_forest_regressor.rs:

// Deprecated in 0.9, still compiles:
let xi = rng.gen_range(0..nrows);
// Clean replacement:
let xi = rng.random_range(0..nrows);

rand::distributions module → rand::distr — if referenced anywhere directly:[^6]

// Deprecated import path
use rand::distributions::Distribution;
// Replacement
use rand::distr::Distribution;

SliceRandom trait split — rand 0.9 splits SliceRandom into IndexedRandom (for choose) and SliceRandom (for shuffle). src/model_selection/mod.rs imports SliceRandom; if it calls choose, an additional import of IndexedRandom is needed.[^1][^6]

rand_distr renamed types — if smartcore's dataset generators use WeightedAliasIndex or DistString, those paths moved:[^6]

  • weighted_alias::WeightedAliasIndexweighted::WeightedAliasIndex
  • Trait DistStringSampleString

Complete Cargo.toml Diff

# [dependencies]
- rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
+ rand = { version = "0.9", default-features = false, features = ["small_rng"] }
- rand_distr = { version = "0.4", optional = true }
+ rand_distr = { version = "0.5", optional = true }

# [target.'cfg(target_arch = "wasm32")'.dependencies]
- getrandom = { version = "0.2.8", optional = true }
+ getrandom = { version = "0.3", optional = true }

# [features]
- js = ["getrandom/js"]
+ js = ["getrandom/wasm_js"]
- stdrand = ["rand/std_rng", "rand/std"]
+ stdrand = ["rand/std_rng", "rand/std", "rand/thread_rng"]

Viability Verdict

The upgrade is viable with low-to-medium effort. The code changes are localized and surgical — primarily in src/rand_custom.rs (one line) and Cargo.toml (four lines). The biggest risk isn't compilation; it's the getrandom js → wasm_js rename breaking WASM users downstream silently, and ensuring the WASM CI target (wasm32-unknown-unknown) is exercised in the test suite to validate the js feature path end-to-end after the rename. The wasm-bindgen-test harness already in dev-dependencies provides that coverage.[^8][^1]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions