Skip to content

feat(util): const-ify the tokio-util builders#7810

Draft
BastiDood wants to merge 3 commits intotokio-rs:masterfrom
BastiDood:constify-tokio-util
Draft

feat(util): const-ify the tokio-util builders#7810
BastiDood wants to merge 3 commits intotokio-rs:masterfrom
BastiDood:constify-tokio-util

Conversation

@BastiDood
Copy link

@BastiDood BastiDood commented Dec 31, 2025

Caution

This PR is marked as a draft for now until the next MSRV bump happens. We need at least Rust 1.83 to support const in mutable contexts.

Motivation

While using the tokio-util crate, I've found myself frequently constructing codec builders from scratch—so much so that I figured it would be mighty helpful if I can just hoist the Builder configuration into a static somewhere (or alternatively, a const codec instance).

But alas, none of the builders are marked as const fn at the moment.

Solution

This PR simply marks many of the builder methods as const. For lack of const support as of the MSRV, some const-ified methods required small refactors like:

  • Inlining cmp::max as explicit if blocks
  • Inlining unwrap_or as explicit match blocks
  • Inlining try_from with an explicit bounds check + as-casts
    • These should be optimized away at runtime, and even more so in const contexts.

Note

When const Drop and const TryFrom stabilizes, we can revert them back to their original formulation.

@github-actions github-actions bot added the R-loom-util Run loom tokio-util tests on this PR label Dec 31, 2025
@BastiDood BastiDood marked this pull request as draft December 31, 2025 02:54
/// assert_eq!(codec.max_length(), 256);
/// ```
pub fn max_length(&self) -> usize {
pub const fn max_length(&self) -> usize {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you want to make a method const when its constructor is not const? (The same applies to several other files.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point; I figured it as mostly a matter of forwards-compatibility when Rust eventually stabilizes wider const support elsewhere. I dream of the day Rust gets powerful const evaluation like Zig's comptime and C++'s constexpr.

For example: some VecDeque methods can be const, but somehow hasn't been const-ified. When cases like that eventually resolve, we can trivially upgrade the constructors to have end-to-end const support.

Note

For this example in particular, though, I just missed adding a const to the constructor. I've amended the commit now. Thanks for pointing it out!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making a method const when it cannot actually be used as such should be avoided if compatibility is in mind, as it prevents us from changing its implementation to something incompatible with const in the future.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I've reverted the const methods without a const constructor in 79e6b1a. Let me know if I've missed anything. 🙇‍♂️

Copy link
Member

@taiki-e taiki-e left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it should be possible to make getter methods that return the mutable reference const as well.


/// Obtain a reference to the handle inside this `TokioContext`.
pub fn handle(&self) -> &Handle {
pub const fn handle(&self) -> &Handle {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a missed const constructor actually. We can safely add pub const fn new. The fix was lumped in with 79e6b1a.

/// # pub fn main() {}
/// ```
pub fn new() -> Builder {
pub const fn new() -> Builder {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think new_codec and new_framed can also be const.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped so too, but unfortunately, they can't because the FramedImpl instantiates RWFrames as its internal state. Since RWFrames invokes BytesMut::with_capacity in its Default implementation, the overall constructor is now non-const (at least until const Default gets stabilized).

Note

Using BytesMut::new won't save us here because it is also non-const by virtue of invoking BytesMut::with_capacity(0) internally.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me clarify: only new_codec can be const-ified (see ed6a13a), but new_framed can't because of the aforementioned BytesMut::with_capacity limitation.

@Darksonn Darksonn added A-tokio-util Area: The tokio-util crate M-codec Module: tokio-util/codec labels Jan 1, 2026
@Darksonn
Copy link
Member

Darksonn commented Jan 1, 2026

This constifies a lot of methods. Can you explain exactly which ones require a higher MSRV? I can see some of them that I'm certain do not require it.

In general, I don't think making a few methods in tokio-util const is a very strong reason to bump the MSRV.

@BastiDood
Copy link
Author

Can you explain exactly which ones require a higher MSRV?

These are mostly the builder methods that set values in mutable contexts (behind &mut self). This PR in particular const-ifies:

  • AnyDelimiterCodec::new_with_max_length
  • length_delimited::Builder::* (basically all the &mut self setters)
  • length_delimited::Builder::new_codec (can be const because adjust_max_frame_len also can now be const)

The rest of them are MSRV-compatible const-ified getters and constructors.

In general, I don't think making a few methods in tokio-util const is a very strong reason to bump the MSRV.

That's understandable. Though, just to give some context: it was not my intention to bump the MSRV just for the sake of fulfilling this feature in isolation; I come at it hoping that the MSRV bump would unlock/spur new features and other refactors within Tokio.

(Admittedly, I haven't looked into the specifics of what those exact "features and refactors" are aside from this PR, but benefiting from one year's worth of new Rust developments sounds like a lot of unlocked possibilities!)

@Darksonn Darksonn added the S-blocked-on-msrv Status: need an MSRV bump to progress label Jan 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio-util Area: The tokio-util crate M-codec Module: tokio-util/codec R-loom-util Run loom tokio-util tests on this PR S-blocked-on-msrv Status: need an MSRV bump to progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants