smartcore: classical machine learning in Rust, with a WASM-first default feature set
A comprehensive library for machine learning and numerical computing. Apply Machine Learning with Rust leveraging first principles.
At a glance
- What is it?
- smartcore is an Apache-2.0 Rust crate covering supervised and unsupervised classical algorithms on top of its own linear algebra traits, with optional ndarray bindings. Its defaults target WASM/WASI builds, and its release cadence is fast enough that pinning a version matters.
- Who is it for?
- Adopt smartcore if you want classical estimators (KNN, random forest, SVM, K-Means, ridge and lasso) callable from Rust code that must also compile to wasm32, and you accept the crate as the owner of your estimator and linear algebra layer. Do not adopt it if your pipeline already lives in Python scikit-learn and Rust is only a deployment target, or if you need deep learning or a dataframe abstraction; smartcore provides neither.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap smartcore fills for Rust binaries that need estimators, not a Python runtime
Rust has good numerical primitives and good dataframe tooling, but it has never had a single obvious home for the middle layer: fit a KNN classifier, cross-validate it, read a CSV into a matrix, score it with macro F1. smartcore targets exactly that layer. The README describes it as "a fast, ergonomic machine learning library for Rust, covering classical supervised and unsupervised methods with a modular linear algebra abstraction and optional ndarray support." The intended user is an engineer who is already writing Rust and does not want to shell out to Python, embed a Python interpreter, or hand-roll a solver. The algorithm list is conventional rather than exotic: linear models (OLS, Ridge, Lasso, ElasticNet, logistic regression), tree methods and ensembles (random forest, Extra Trees), SVM with SVC/SVR and multiclass extensions, KNN classification and regression, clustering (K-Means with K-Means++ initialization, DBSCAN, agglomerative including single-linkage), naive Bayes in four variants, matrix decompositions (SVD, EVD, Cholesky, LU, QR), plus preprocessing, encoders, split utilities, K-fold, search parameters, and metrics. That is a scikit-learn-shaped surface delivered as a crate rather than a runtime.
DenseMatrix, linear algebra traits, and where ndarray fits
The mechanism visible in the README is a linear algebra abstraction with traits at the center. Data enters through DenseMatrix, which the quick start builds from nested slices with DenseMatrix::from_2d_array, returning a Result. Labels stay as plain Vec<T>. Estimators are constructed through a fit associated function that takes the matrix, the labels, and a parameters struct defaulted with Default::default(). Prediction is a method on the fitted model. The README's example is short enough to quote the shape of it: KNNClassifier::fit(&x, &y, Default::default()).unwrap() followed by knn.predict(&x).unwrap(). Because the traits are the abstraction point, ndarray integration is optional rather than assumed, and the README lists ndarray-bindings as an optional feature while noting it is "deprecated in favor of ndarray-only support per recent changes." The roadmap section goes further and states that some bindings, nalgebra-bindings among them, have been dropped in favor of ndarray-only paths. That is a real architectural commitment: if you built against a nalgebra path, the migration is on you, and the README points to CHANGELOG.md for deprecations and breaking changes rather than enumerating them inline. The roadmap also mentions a trait-system refactor toward "fewer structs and more object-safe traits," which affects how you store models behind dyn references. Determinism is addressed through RNG plumbing for seeds, which matters for K-Means and forest methods where initialization drives results.
Installing it and the feature flags you actually have to choose
The install path is a single Cargo.toml line: smartcore = "^0.6". For unreleased changes there is a git form pointing at the main branch: smartcore = { git = "https://github.com/smartcorelib/smartcore", branch = "main" }. The README names three optional features explicitly: datasets, serde, and ndarray-bindings, and then tells you to check Cargo.toml for the authoritative list and compatibility notes. That instruction is worth taking literally, because the README also says features are opt-in and that defaults are tuned for WASM/WASI builds, with serde and datasets deliberately off. The practical consequence is that a native build which wants the built-in sample datasets (digits, diabetes, breast cancer, boston) must turn the datasets feature on, and any build that wants to serialize a fitted model must turn serde on. The README is explicit that enabling serde selectively is how you keep footprint down. On the data side, CSV readers accept a configurable delimiter and header rows, and the README notes non-IO reader abstractions alongside the file-based ones, which is the hook for feeding data from a string or a network buffer. Dataset generators make_blobs, make_circles, and make_moons cover quick experiments without external files. For interactive exploration, the recommended route is the companion smartcore-jupyter repository with EVCXR for Rust notebooks, which the README calls the recommended path to experiment with the API.
The WASM-first default is a constraint, not a bonus
Most Rust crates treat WebAssembly as a target they tolerate. smartcore inverts that: the README states it "adopts a WASM/WASI-first posture in defaults to ease browser and embedded deployments," and that some file-system operations are restricted in wasm targets, with tests and IO utilities structured to avoid unsupported calls where possible. Read that as a warning about the native path too. The CSV readers and the built-in dataset loaders are the parts most likely to touch the file system, and the README's phrasing ("where possible") leaves room for cases it does not enumerate, deferring target-specific caveats to module-level docs and CHANGELOG. If your workload is a server-side batch job that reads a 2 GB CSV and fits a random forest, the WASM-first default buys you nothing and the feature gating costs you a configuration step. The flip side is genuine: if you are shipping a browser-side or embedded inference path, you are not fighting the crate's defaults to get there. The other constraint worth naming is the edition bump. The roadmap states a move to Rust 2024 edition with MSRV 1.85. If your toolchain is pinned below that, smartcore 0.6.x is not available to you at all, regardless of features.
Version churn: three releases in three days and what that implies for pinning
The release list shows v0.6.12 on 2026-08-24, v0.6.13 on 2026-08-25, and v0.6.14 on 2026-08-26, with the repository's last push timestamped the same day as the newest release. That is patch-level churn at a rate that suggests active refactoring rather than occasional bug fixes, and the roadmap corroborates it: a trait-system refactor, a codebase reorganization, cleanup of duplicate code paths, tree and forest component refactoring, SVM multiclass support, an XGBoost-style regression addition, and single-linkage clustering. The README also records a metrics change that is behaviorally visible: multiclass macro F1 now averages per-class F-measures to match sklearn, and Precision/Recall/F1 share a single per-class confusion-counts helper. If you computed macro F1 with an earlier 0.6.x, your numbers can move without any change to your data or model, purely from the metric definition. The caret requirement smartcore = "^0.6" will pull these patches automatically. Given the stated API changes and migration notes living in CHANGELOG.md, pinning an exact version and reading that file before each bump is the lower-risk posture. On maintenance cost more broadly: the crate is Apache-2.0, which permits commercial and closed-source use and includes an explicit patent grant. Apache-2.0 imposes notice and attribution obligations on redistribution, and it is not a copyleft licence, so it does not force you to open your own code. That is a general description of the licence text, not legal advice; check the LICENSE file in the repository and your own counsel for your situation.
When smartcore is the wrong layer: the Polars and ndarray alternative
The honest alternative is not another ML crate. It is Polars for tabular work plus ndarray for the numerics, writing the estimator you need yourself. The difference in approach is structural. smartcore owns the whole stack: it defines the matrix type, the linear algebra traits, the estimator traits, the metrics, and the CSV reading, so you get a consistent API across algorithms and you get them all at once. Polars plus ndarray gives you a fast columnar engine, lazy query planning, and a general n-dimensional array, but no estimators. You would implement ridge regression, K-fold splitting, and macro F1 yourself, and you would maintain them. For a team that needs one linear model and already has Polars in the dependency tree, adding smartcore means pulling in a second matrix abstraction and a second set of traits for a small amount of functionality. For a team that needs six algorithms, cross-validation, and a metrics suite, writing those by hand is the larger cost. There is also the Python boundary to consider. If your training pipeline is scikit-learn and Rust is only the serving path, smartcore does not solve your problem: it does not import scikit-learn models, and the README describes no ONNX or PMML path. You would be retraining in Rust or exporting coefficients by hand. Finally, smartcore is classical methods only. There is no neural network module in the algorithm list, so a project that needs gradient-based deep learning should look elsewhere regardless of language preference.
Benchmarks, determinism, and what the repository does not tell you
The README documents a benchmarking setup rather than publishing numbers. Two tools run on the gh-pages branch and are rendered as interactive charts by benchmark-action/github-action-benchmark: criterion for wall-clock time per bench in ns/iter, with a 200 percent advisory alert that posts a comment and does not fail CI, and iai-callgrind for instructions retired, which the README describes as deterministic and machine-independent, with a 120 percent alert that does fail the iai job and set a status. The distinction is useful for anyone evaluating the project: the instruction-count chart is the one that compares across machines, and the wall-clock chart is advisory by design. Note what is absent. The README gives no absolute performance figures, no accuracy comparisons against scikit-learn beyond the macro F1 averaging note, and no statement about memory use. Determinism is claimed through seed and RNG plumbing in the roadmap, but the README does not document per-algorithm seed semantics, so if reproducibility across runs is a requirement, verify it per estimator against the API docs rather than assuming uniform behavior. The same caution applies to the WASM claim: the README says IO utilities are structured to avoid unsupported calls "where possible," which is not the same as a guarantee for every reader and dataset loader on every target.
Editorial conclusion
Adopt smartcore if you want classical estimators (KNN, random forest, SVM, K-Means, ridge and lasso) callable from Rust code that must also compile to wasm32, and you accept the crate as the owner of your estimator and linear algebra layer. Do not adopt it if your pipeline already lives in Python scikit-learn and Rust is only a deployment target, or if you need deep learning or a dataframe abstraction; smartcore provides neither. Before committing, verify three things against the version you pin: whether the ndarray-bindings feature is still present or already removed in favor of the ndarray-only path, whether your target's file-system restrictions break the CSV readers and built-in dataset loaders, and what CHANGELOG.md says about the trait-system refactor and the Rust 2024 edition bump to MSRV 1.85.
Community notes