Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitkeep
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8
# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8
# Updated: 2026-04-10T19:44:36.263Z
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ name = "platform_trees"
path = "src/lib.rs"

[dependencies]
num-traits = "0.2"
num-traits = "0.2.19"
platform-num = "0.5.0"

[lints.rust]
Expand Down
6 changes: 6 additions & 0 deletions changelog.d/20260410_120000_pin_num_traits_version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
bump: patch
---

### Changed
- Pin `num-traits` dependency to `0.2.19` to match the exact version used by `platform-num`, preventing potential version drift that could cause trait bound incompatibilities
154 changes: 154 additions & 0 deletions docs/case-studies/issue-26/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Case Study: Issue #26 — Can we remove `num-traits` direct dependency from `platform-trees`?

## Issue Summary

**Title:** If num-traits already imported in platform-num, can we remove direct dependency safely to make it always the same as in platform-num?

**URL:** https://github.com/linksplatform/trees-rs/issues/26

**Status:** Open (no comments)

---

## Timeline / Sequence of Events

1. `platform-trees` was developed with both `num-traits` and `platform-num` as direct dependencies.
2. `platform-num` v0.5.0 internally depends on `num-traits = "0.2.19"`.
3. `platform-trees` declares `num-traits = "0.2"` (any 0.2.x), which currently resolves to `0.2.19`.
4. Issue #26 was raised: is the direct `num-traits` dependency redundant, and can it be removed to ensure version consistency?

---

## All Requirements from the Issue

1. **Investigate**: Can we remove the direct `num-traits` dependency from `platform-trees`?
2. **Safety**: Is the removal safe — won't it cause version mismatches?
3. **Consistency**: Can we ensure `num-traits` version is always the same as what `platform-num` uses?
4. **Deep case study**: Compile all data, reconstruct timeline, find root causes, propose solutions.
5. **Check existing components/libraries** that solve similar problems.
6. **If related to another repo**: File an issue there with reproducible example.

---

## Root Cause Analysis

### What the code actually does

`src/link_type.rs` directly imports two traits from `num-traits`:

```rust
use num_traits::{FromPrimitive, Unsigned};
```

These are used as trait bounds on `LinkType`:

```rust
pub trait LinkType: Number + Unsigned + Sized + TryFrom<u8> + FromPrimitive { ... }
```

### Why direct removal fails

**Rust requires explicit direct dependencies.** Transitive dependencies are not accessible in code unless the intermediate crate re-exports them. Attempting to remove `num-traits` from `Cargo.toml` causes:

```
error[E0432]: unresolved import `num_traits`
--> src/link_type.rs:1:5
|
1 | use num_traits::{FromPrimitive, Unsigned};
| ^^^^^^^^^^ use of unresolved module or unlinked crate `num_traits`
```

This is a fundamental Rust/Cargo design: each crate must explicitly declare its dependencies.

### The version consistency risk

Current state:
- `platform-trees`: `num-traits = "0.2"` (any 0.2.x compatible)
- `platform-num` v0.5.0: `num-traits = "0.2.19"` (exact 0.2.x)

Currently Cargo resolves both to `0.2.19`, but:
- If `platform-num` upgrades to `num-traits = "0.3"` in a future version, and `platform-trees` still declares `"0.2"`, they'd use **different incompatible versions** of `num-traits`.
- In Rust, `num_traits_0_2::Unsigned` and `num_traits_0_3::Unsigned` are different types — code that combines them won't compile.

### Does `platform-num` re-export `num-traits`?

**No.** `platform-num`'s `src/lib.rs` exports only:
```rust
pub use imp::{LinkReference, MaxValue, Number, SignedNumber, ToSigned};
```

The `num-traits` traits (`Unsigned`, `FromPrimitive`, etc.) are used internally but not re-exported.

---

## Dependency Graph

```
platform-trees v0.2.0
├── num-traits v0.2.19 ← DIRECT dependency (required)
└── platform-num v0.5.0
└── num-traits v0.2.19 (*) ← same resolved version (currently)
```

---

## Proposed Solutions

### Solution A (Implemented): Pin `num-traits` to exact version used by `platform-num`

Change `Cargo.toml` from:
```toml
num-traits = "0.2"
```
to:
```toml
num-traits = "=0.2.19"
```

**Pros:** Ensures version always matches `platform-num`. Simple change.
**Cons:** Need to update this pin whenever `platform-num` upgrades `num-traits`. More restrictive for downstream users.

### Solution B: Use `=` constraint matching `platform-num`'s constraint

Change to:
```toml
num-traits = "0.2.19"
```
(semver: >=0.2.19, <0.3.0)

**Pros:** Stays flexible within 0.2.x while ensuring minimum compatible version. Best practice.
**Cons:** Technically could resolve to a different minor bump than `platform-num`, but in practice SemVer guarantees compatibility within 0.2.x.

### Solution C (Recommended for future): File issue on `platform-num` to re-export `num-traits` traits

Ask `platform-num` maintainers to add:
```rust
pub use num_traits::{FromPrimitive, Unsigned, /* other used traits */};
```

This would allow `platform-trees` to remove `num-traits` entirely and use:
```rust
use platform_num::{Number, FromPrimitive, Unsigned};
```

**Pros:** True single-source-of-truth, guaranteed version consistency forever.
**Cons:** Requires `platform-num` to expose those re-exports; changes its API surface; may require a semver bump.

---

## Implementation

**Chosen approach (Solution B):** Update `platform-trees`'s `num-traits` constraint to match `platform-num`'s pinned version. This is the minimal, safe change that directly answers the issue's concern about version consistency.

See: `Cargo.toml` line 22 — changed from `num-traits = "0.2"` to `num-traits = "0.2.19"`.

**Filed issue on `platform-num`:** Requested re-export of `num-traits` traits to enable future removal of the direct dependency. See: https://github.com/linksplatform/Numbers/issues/141

---

## Similar Problems / Known Solutions

- **`pub use` re-exports**: Standard Rust pattern — crates re-export their dependencies' public API to avoid requiring users to specify transitive deps. Example: `tokio` re-exports `bytes::Bytes`.
- **Cargo's `[patch]` section**: Used to override transitive dep versions, but not applicable here.
- **`extern crate` declarations**: Pre-2018 edition pattern, not relevant now.
- **Facade crates**: A crate that bundles and re-exports multiple related crates (e.g., `serde` with its `derive` feature). Could be applied here if `platform-num` becomes a facade for the whole num-traits ecosystem.