cd /news/developer-tools/work-in-progress-rust · home topics developer-tools article
[ARTICLE · art-48217] src=blog.dureuill.net ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Work In Progress Rust

A Rust developer at Meilisearch presents techniques for deferring error handling during development, such as using `unwrap` and `clone` to bypass compilation errors temporarily, and relying on CI with `-D warning` to catch warnings before merging. The approach allows developers to stay in the "happy path" while prototyping, then address correctness later.

read10 min views1 publishedJul 5, 2026
Work In Progress Rust
Image: source

Rust is a reliable language that prioritizes correctness. However, during development, it can be a burden to account for every single error path immediately.

This article presents a few techniques that defer handling correctness to that you can stay longer in the happy path, as well as a library I developed to make this process more convenient.

🌱 100% of this article has been handcrafted without using generative AI tooling, if somewhat hastily. Take my typoes and broken sentences as proof of that.

What's worse for a Rust developer working on some elaborate logic than having rustc

suddenly getting in the way? Be it because you didn't return a Result

yet, or handle this error case, or implement that edge case, causes for compilation errors are numerous and can distract you from what you're attempting to achieve.

When this happens you're usually presented with two options:

  • Go with the distraction and immediately implement perfect error handling. Typically you'll have to refactor it a number of times before the PR is ready, making the whole process much more heavy than what is necessary.
  • Slap some manner of TODO later

label on the error, downgrading it to a warning, and move on with your work for now.

This article covers some Rust techniques to achieve the latter option, downgrading an immediate error to a warning to be handled later.

Why do you want warnings?

Warnings occupy the perfect spot in the possible gamut of compilation interactions:

Errors block other compilation steps (lifetimes, etc), and testing, impeding the common workflow:

  • Write WIP code
  • Test it
  • Turn WIP code to production code

Any error prevents going from (1) to (2)

Completely suppressing any compilation message, on the other hand, runs the risk of forgetting about the issues and letting them sweep into production 😬

Warnings don't block development, but need to be cleaned before merging the PR.

At Meilisearch, we have a CI that runs the compiler with the

-D warning

flag, that will turn all warnings into errors at CI time, ensuring we don't ship WIP code accidentally.

Why don't you just read the f**king code???

Of course, I do. Reading code (yours, and the one from your colleagues) is an excellent barrier against bugs, and code reviews are a requirement to effectively share knowledge among your team.

All the techniques discussed in the article come as an additional manner of creating incentives for correctness and stronger guardrails when producing code at a professional scale. They increase quality but don't replace the basic good practices.

In my experience as a software engineer, anything that relies on humans never a mistake is deemed to fail eventually.

Without further ado, here are some techniques you can use in vanilla Rust to implement error deferral strategies.

Techniques with standard Rust

unwrap

everywhere

unwrap

everywhereWhile it is important that Rust gives us the structure to correctly address errors, it doesn't mean we need to address them immediately.

A common pattern for delaying the error handling is to simply unwrap

your Result

or Option

, to get the value inside at the price of a panic on the error path.

Then when the rest of the code is ready, you can go back to your changes and selectively remove the unwrap

that were added as a temporary measure, replacing them with proper error handling.

fn does_not_return_result_yet(some_params: SomeType) -> SomeReturnType {
  let converted = some_params.try_into().unwrap() // <- fallible conversion, ignore for now
  converted.try_foo().unwrap() // <- fallible operation, ignore for now
}

clone

everywhere

clone

everywhereOwnership is a similar issue, albeit slightly different and much less common than error handling. Properly maintaining ownership sometimes mandates design modifications, lest lifetime issues appear. In this case, a frequent mitigation option is to clone a value instead of moving it around.

Delaying the fixes for this category of issues is a bit more delicate, as the fix can entail changing the architecture of the code in non-trivial ways. Still, with experience, it is possible to identify early cases where you'll be able to remove the clones later by passing the appropriate variables higher in the stack.

the todo!

macro

todo!

macroThe Rust standard library provides a todo!() macro, whose purpose is described as:

Indicates unfinished code.

This can be useful if you are prototyping and just want a placeholder to let your code pass type analysis.

This is indeed extremely useful when shaping out an interface and needing to make some function definitions available, without actually having to provide a working implementation.

Because todo!

diverges (it panics), the compiler is able to use it as a placeholder expression for most types, hence the reference to type analysis in the docs. A liberal use of todo

allows deferring some code paths (and not just errors) to be implemented at a later time.

Digression: not to be confused with unimplemented!()

unimplemented!()

I saw at least once someone being wrong on the Internet about todo!()

: they would state that it was meant to communicate to users of a program that some features were not yet implemented.

Rust actually has a dedicated macro for this use case, and its name is unimplemented!().

todo!()

has a different audience: the author of the code and their reviewers. No todo

s are meant to hit production, they are a WIP Rust amenity only.

mut

, unused variables, etc.

mut

, unused variables, etc.The default warnings provided by the compiler are very useful while developing. They will catch:

  • unused bindings
  • needlessly mutable bindings
  • unused results and options

All of these tend to appear when code isn't finished developing. For instance, a function whose implementation consists solely of todo!()

will typically have such warnings about their parameters.

⚠️It is important to resist the urge to temporarily fix these warnings during development, by prefixing the bindings with _

or other tricks. You risk handing over the unfinished code if you artifically fix the warnings. Ignoring warnings has its place, but it ain't that!

Leaving a memo: the doc comment trick

It often happens while I'm coding that I think of something that needs fixing or handling, like an edge case or some incomplete/broken logic. In contrast with explicit error handling, this kind of issues is not tracked in the type system, so I usually leave a comment with a reminder to fix the issue, if I don't want a distraction from the logic I'm currently implementing.

This is fine and dandy, but I need to remember to actually go back there after the fact 😅

As a means to do this, I often use a trick where such comments are actually prefixed with a triple slash, as if they were documentation comments.

Rust warns about documentation comments that don't document anything, so putting a documentation comment above a statement generally achieve the goal of generating a warning associated with the issue described in the comment.

Where are my warnings? Limitations of vanilla Rust

These techniques served me well for years (I've been writing Rust for more than a decade now...), however I have my grievances with them.

Generally, the most aggravating issue is that not all of these techniques reliably generate a warning associated with the issue that needs solving before sending the code into production.

Forgetting to remove an unwrap

and watching it burst into flames after the release is one of the worst feelings you can get as an engineer 😓

todo!

is a frequent and weird offender in this category. As stated earlier, it often results in warnings, but these are indirect and caused by e.g. function parameters going unused or mutation not taking place when it should. Because of this, it can become a bit treacherous, as one is used to often have a warning when using todo!

and rely on their absence to hit the merge button.

unwrap

and clone

are even worse, they never emit warnings, so you have to be extra careful in re-reading to remove them before the final PR.

An additional issue with them is that some of them are meant to reach production. With the "unwrap liberally" strategy outlined in this article, we are conflating the normal use case of unwrap with its "WIP" use case.

Lastly, "TODO doc strings" are kind of a hack, perhaps we could say it is a happy little accident? In this capacity, it has unintuitive failure modes, because doc comments are currently forbidden on expressions, so will cause a compilation error. Doesn't frequently happen, but we also run the risk of using the ///

in locations where they actually document something, which isn't exactly nice to ship and won't have the expected warning.

Introducing the wip

crate

wip

crateIn response to all these small pains for writing WIP Rust, I decided to develop a crate called wip to make it easier to implement the "warnings for later" strategy.

Mostly it consists of a collection of tools you can use in every context while you're developing Rust.

Using wip

wip

Add wip

to your dependencies like you would for any other crate:

cargo add wip

Optionally, you can import the prelude provided by wip

in the files where you want to use wip

:

use wip::prelude::*;

As wip

relies on the deprecation warnings of Rust, I suggest you disable the feature of your editor to strike calls to deprecated functions, otherwise it becomes visually invasive.

wip::wip!

, a warning-emitting todo!

wip::wip!

, a warning-emitting todo!

The first tool provided by wip

is a wip!

macro, that works exactly like todo!

, but always emits a warning on use. Use it everywhere you'd use todo!

, to mark code as unfinished.

Why not todo!

?

todo!

?Earlier versions of wip

used the todo

naming, however it would prevent us from using the prelude, as Rust doesn't like it when a dependency attempts to shadow an std

macro...

unwrap_wip

and clone_wip

unwrap_wip

and clone_wip

These methods are implemented as extension traits on Result

and Option

. When in scope, they work exactly like the regular unwrap

and clone

, except that they emit a warning on usage.

They also clearly signal that you don't mean for the unwrap/clone to stay in final code! Although, keep in mind it doesn't always end up happening that way...

wip::fixme!

, a non-panicking macro for memos

wip::fixme!

, a non-panicking macro for memosOne of the tools I use the most from wip

is the fixme!

macro. It is a non-panicking variant of wip!

, that doesn't stand for an expression. It works the same as TODO comments, sans the hackish part and with a reliable warning.

Embracing our WIP future

I have been using the wip

crate for a few of the my latests PRs, including some of my more unreasonably large ones. Usually I add the wip

dependency to crates in the workspace during development, and then remove it when rewriting history before review.

It has been a liberating experience, squarely falling in the category of "I didn't expect to need it that much". It makes focusing on the happy path much more straightforward, while not forgetting things for later.

What's not to love?

Only drawbacks I can think of:

  • One needs to write enough context when adding a fixme

, otherwise they can become hard to fix when coming back much later. This is usually not a problem, aswip

are usually contained to (usually) short PRs by a single author (or pair programmed). - Sometimes the number of wip

-induced warnings can become a bit discouraging, again especially on larger PRs. Perhaps avoid those 😅

Still, I'm quite sure I'll keep using wip

. How do you use warnings during development? What do you think would be some useful additions to the current API?

── more in #developer-tools 4 stories · sorted by recency
── more on @meilisearch 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/work-in-progress-rus…] indexed:0 read:10min 2026-07-05 ·