benjajaja / ratatui-image

Ratatui widget for rendering image graphics in terminals that support it
https://crates.io/crates/ratatui-image
MIT License
110 stars 16 forks source link

semver hazards #35

Closed joshka closed 2 weeks ago

joshka commented 2 months ago

TL;DR - please consider changing the version specifiers away from naive comparison >= to semver compatible ^ (which can be omitted) for at least Ratatui and crossterm. Also, on the crossterm / termion / termwize dependency, it's now possible to point at ratatui::crossterm etc. to get the major version of the backend that's compatible with Ratatui rather than specifying this in your dependencies.

Background reading

Problem

Specifying a version in the cargo toml using >= causes downstream headaches. We saw this happen frequently in tui-textarea, and ansi-to-tui, and it seem like this is happening here too. Discord thread from the Rust discord: https://discord.com/channels/442252698964721669/448238009733742612/1273527249857024062

Apologies as I'm sure this is a common question, but I'm having trouble finding answers elsewhere.

I have two dependencies listed in my Cargo.toml:

ratatui = "=0.27.0"
ratatui-image = "=1.0.5"

and in ratatui-image:

ratatui = ">=0.23"

My understanding was that cargo was smart enough to pick a version of ratatui that would satisfy both requirements, but in my case when I run cargo build, even after deleting Cargo.lock or running cargo clean, I get errors complaining that Rect is not equal to Rect (the versions of Rect from ratatui 0.27.0 and ratatui 0.28.0 respectively). cargo tree shows that ratatui-image is pulling in 0.28 and my own package is pulling in 0.27. Is there some gotcha here that I'm not understanding about the cargo dep tree?

The issue is that an app that uses ratatui-image will pull in the latest version of ratatui (and crossterm, ...) whenever that's updated (e.g. 0.28.1), but will also pull in the version that fits the application's version selector (e.g. 0.27.0). The types in each version have the same name but are incompatible with each other (even if they are unchanged). Effectively 0.x is a major version. This problem is documented in https://doc.rust-lang.org/cargo/reference/resolver.html#version-incompatibility-hazards

I'd recommend avoiding specifying dependencies as >= and instead lock the version to whatever major version that makes sense. We do make breaking changes in between major versions in Ratatui, generally for good reasons, and we try to document them as well as possible. But specifying that a library is compatible with any major version of another library when the dependency appears in the public API, or where the library depends on some internal shared state (e.g. crossterm stores termios to restore raw mode) is a problem that causes downstream pain.

I haven't looked at other dependencies in the cargo.toml, but I suspect this issue may cause pain there also.

Last, you might find that your users appreciate a major version bump when bumping the major versions of dependencies that appear in your public API. This prevents them automatically breaking when you release a semver incompatible update marked as semver compatible. (This is something I learnt myself in tui-widgets). See https://github.com/joshka/tui-widgets/issues/31 for info on this problem. My action there was to yank the latest release and publish a new major version instead.

Admittedly these are the edge cases which cause pain points with semver, but where would we be if everything was easy? :D

joshka commented 2 months ago

ping @cxreiff