LightPhotos

Why Rust

Should I write the whole app in Rust, or put a web or Swift interface on top of a Rust engine? Here is what I weighed and what decided it.

ONE SLIDER DRAG Slider moves Edit function New pixels events cross pixels cross No runtime boundary. It is a function call. ONE SECOND OF DRAG = 60 FRAMES Frame drawn Boundary cross

All Rust: 0 frames, 0 boundary crosses. Drag the slider or press Run.

Each slider movement makes one frame. The Rust engine is equally fast in the two designs. The split design adds two crossings to each frame.

What I learned

Why I considered Rust

I think that Rust is a very interesting language. It is roughly as powerful as C++, and its strict compiler refuses to build code that looks unsafe. It feels like a picky reviewer who sits next to me.

But that does not make Rust the right choice for a photo editor. I had many questions:

I also wanted a browser version one day, so that I can edit without my Mac. I might also want to support Windows and Linux one day.

The cost of each option

All Rust gives me one language. I do not copy image data back and forth between two programs, or pass each click and drag across a bridge between them. The app is faster, needs fewer external libraries, and is simpler to ship.

But Rust has no official UI toolkit. The community libraries work, but my UI will not look as good as a well-styled web page or a native Mac app. Browsers give good accessibility (support for screen readers and similar tools) by default. The Rust UI libraries are far behind.

A split design (for example, a React UI in Electron) lets me use the best tools on each side. With CSS, I control each pixel. The cost is two systems that I must keep in sync.

The Swift option

A Swift interface feels the most natural on a Mac. But it is also a split: Swift communicates with a Rust engine. Swift does not run in the browser, so a browser version (made with WebAssembly, which lets compiled code run in a web page) needs a second interface, written from scratch. Swift also ties me to Apple: Windows or Linux needs one more. The Rust UI libraries and Electron already run on all three.

Comparison

All Rust Rust + React Rust + Swift
Languages 1 2 2
Engine and UI to keep in sync No Yes Yes
Extra runtime to ship None Electron None
Appearance Plain Anything CSS can do Native Mac
Accessibility Limited Browser-grade Native Mac
Browser (WASM) version Same code Engine to WASM, UI as is Needs a second UI
Windows and Linux Same code Same code Needs another UI

Why the handoff decided it

In a web app, people expect a short delay. A photo editor is different. Drag, zoom, and pan are continuous. Each mouse movement must reach the engine and come back as a new image, with no lag that you can feel.

To keep two systems in step at that rate is much more difficult than to handle an occasional request. When they get out of step, the bug usually sits in the handoff, where the code on each side looks correct on its own. So this type of bug is very difficult to track down.

Coding agents wrote most of this code. In my experience, they are strong when all of the code is in one language. They are much weaker when they must find a small mismatch between two systems in different languages. With one language and one program, there is no handoff where this bug can occur. When the agents get stuck, I can find the problem myself.

The second reason was performance. I want the app to be fast and to use only the memory that it needs. All Rust has no second runtime to load, and no copies of the image go back and forth across a runtime boundary.

How I keep the option to change

I keep the UI and the photo editing core in separate modules, so I can swap the interface later. If I want a different interface in a year, most of the core comes with me without changes. For the same reason, I clean up sloppy generated code when it arrives. I want the code to stay small enough that I can understand all of it.