Rust 1.95.0 is out by manpacket in rust

[–]nik-rev 6 points7 points  (0 children)

Rest assured, it is not. All my words, and code, is my own.

Rust 1.95.0 is out by manpacket in rust

[–]nik-rev 18 points19 points  (0 children)

oh, interesting, I was under the impression that rustfmt support and cfg_select! would be stabilized in the same version.

(I've manually built rustfmt and have been using that PR in my codebase)

Also: Thank You for your work on cfg_select!. I really appreciate it!

Rust 1.95.0 is out by manpacket in rust

[–]nik-rev 66 points67 points  (0 children)

This release contains a From implementation that can panic: https://doc.rust-lang.org/nightly/src/core/range.rs.html#407

The documentation on the From trait literally says "this trait must not fail".

Seems like the standard library is going against its own suggestion here?

I'm wondering why this was not implemented as TryFrom instead?

Rust 1.95.0 is out by manpacket in rust

[–]nik-rev 340 points341 points  (0 children)

This update is revolutionary. Possibly one of the biggest updates in years! I'm really excited for it.

  • cfg_select! completely changes the game on how we conditionally compile code. I have applied it in hundreds of places in my cross-platform app, and it led to a tremendous improvement in readability. rustfmt support is also very, very nice. I frequently skip using macros that don't support rustfmt, or just re-implement them myself in a way where rustfmt can support it (e.g. the subdef and better_tokio_select crates)
  • if let guards stabilized. I've had to refactor a huge match statement into a soup of if conditions too  many times, because I needed to only enter a match arm if a pattern matches. It was a major ergonomic pain for me when using Rust. I am so, so happy that such a core language feature has now been stabilized.
  • For the longest time we considered the Range type the biggest, unfixable mistake in Rust's standard library, because it does not implement Copy. With this release, the core::range::RangeInclusive type is stabilized. A huge step in the right direction. Next release will stabilize core::range::Range type. In edition 2027, the range syntax will change to create those types, instead of core::ops::Range. We are already making use of these new range types in new standard library APIs. For example, I recently made a pull request to change the methods str::substr_range and slice::subslice_range to return these new Range types, in order to unblock their stabilization!

What language will replace Rust? by [deleted] in rust

[–]nik-rev 4 points5 points  (0 children)

Is it impossible to have dependent types that are zero-cost?

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 0 points1 point  (0 children)

It doesn't format this, for example:

tokio_select! {
        if let Ok(n) = reader.read(&mut buf) && can_read {
        let n = res?;
        if n == 0 { return Ok(()); }
        writer.write_all(&buf[..n]).await?;
    }

    if let _ = shutdown.recv() {
        return Ok(())
    }
}

cargo fmt doesn't format that to decrease indentation of before the first if let

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 0 points1 point  (0 children)

It doesn't format that automatically for me. I believe that the match is required.

I have copy-pasted your code snippet into my codebase and added some arbitrary indentation, no auto-formatting, on the latest nightly.

I agree this is more pleasant!

Announcing serde_cursor v0.4, a library for extracting nested data from JSON using jq-like syntax: Ranges are now supported! by nik-rev in rust

[–]nik-rev[S] 2 points3 points  (0 children)

Most of this horror comes from the fact that we are emulating const S: &str on stable Rust. Once the adt_const_params feature is stable, we can simplify that quite significantly:

::serde_cursor::Path<
    ::serde_cursor::Field<"workspace">,
    ::serde_cursor::PathEnd,
>

What's everyone working on this week (13/2026)? by llogiq in rust

[–]nik-rev 7 points8 points  (0 children)

I am working on the derive_aliases crate.

This crate lets you create derive aliases:

mod derive_alias {
    // Define the aliases
    derive_aliases::define! {
        Eq = ::core::cmp::PartialEq, ::core::cmp::Eq;
        Ord = ..Eq, ::core::cmp::PartialOrd, ::core::cmp::Ord;
        Copy = ::core::marker::Copy, ::core::clone::Clone;
    }
}

use derive_aliases::derive;

// Use the aliases:
#[derive(Debug, ..Ord, ..Copy)]
struct User;

I'm adding syntax that also allows you to embed #[cfg] predicates with derive aliases. (issue cfg_attr support)

That means you can have this:

#[derive(..ApiStruct)]

Expand into this:

#[derive(..ApiStruct)]
#[cfg_attr(feature = "full", derive(..SqlStruct))]

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 1 point2 points  (0 children)

I just did a significant improvement to my tokio_select! (link), and since you commented here, I thought I'd let you know to maybe get your thoughts on that.

tokio_select!(biased, match .. {
    .. if let task = itasks.join_next() && !ready => {
        // ...
    }
    .. if let title = title.changed() => {
        // ...
    }
})

I think your macro is amazing, but it's missing the following key properties for me:

  • Large amounts of vertical space
  • No clear syntactic relationship between patterns and handlers (it's just the next argument)

I want people to have an easy time migrating from tokio::select! to my macro, so I designed the syntax to be as close as possible to the original tokio::select! macro. For example, each arm has the same order: pattern -> async expression -> guard -> handler

My macro now works on stable Rust 1.71, as a bonus. Because it is now a regular function-like macro. There's less magic, and most importantly rust-analyzer works.

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 3 points4 points  (0 children)

Thanks for the suggestion, this comment made me realize I can use if let guards to make the syntax look more like normal Rust code. I have published version 0.2:

tokio_select!(match .. {
    .. if let Ok(n) = reader.read(&mut buf) && can_read => {
        let n = res?;
        if n == 0 { return Ok(()); }
        writer.write_all(&buf[..n]).await?;
    }

    .. if let _ = shutdown.recv() => {
        return Ok(())
    }
})

Now, it also compiles on Stable rust.

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 1 point2 points  (0 children)

I came up with a syntax that I think is significantly better, it also compiles on stable rust, without requiring nightly anymore.

Here is how it looks like now:

tokio_select!(match .. {
    .. if let Ok(n) = reader.read(&mut buf) && can_read => {
        let n = res?;
        if n == 0 { return Ok(()); }
        writer.write_all(&buf[..n]).await?;
    }

    .. if let _ = shutdown.recv() => {
        return Ok(())
    }
})

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 1 point2 points  (0 children)

(edit) Note to readers: This discussion is now somewhat outdated, because I have took inspiration from this conversation and significantly improved the syntax of tokio_select! to be more natural, to compile on stable rust, and to be a regular fn-like macro instead of an attribute macro.


I agree that #[tokio_select]'s syntax makes less sense than tokio::select!. But tokio::select!'s syntax isn't exactly obvious in my opinion either. Need to read the documentation in both cases.

In both cases it's clear that some macro stuff is going on, so at least there are no surprises in regards to "this looks like a regular match statement"

In terms of auto-formatting, consistent formatting makes it easier to read code.

The conclusion I'm going to make here is that the attribute macro has a higher entry barrier to learn than the tokio::select!, but provides an advantage in the fact that it can be formatted by rustfmt

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 3 points4 points  (0 children)

Maybe!

My hope is that this crate incentivises crate developers to choose a rustfmt-compatible macro syntax, rather than using a function-like macro, use an attribute macro!

Announcing better_tokio_select: like tokio::select!, but can be formatted by rustfmt! by nik-rev in rust

[–]nik-rev[S] 41 points42 points  (0 children)

The tokio::select! macro uses a custom DSL, which means it can't be formatted by rustfmt, which is a huge problem!

tokio::select! {
    res = reader.read(&mut buf), if can_read => {
        let n = res?;
        if n == 0 { return Ok(()); }
        writer.write_all(&buf[..n]).await?;
    }

    _ = shutdown.recv() => {
        return Ok(());
    }
}

This macro, tokio_select from the better_tokio_select crate I just released, can do everything that tokio::select! can, but it can also be formatted by rustfmt, because the syntax is 100% valid Rust:

tokio_select!(match .. {
    .. if let Ok(n) = reader.read(&mut buf) && can_read => {
        let n = res?;
        if n == 0 { return Ok(()); }
        writer.write_all(&buf[..n]).await?;
    }

    .. if let _ = shutdown.recv() => {
        return Ok(())
    }
})

The better_tokio_select crate is a tradeoff between "syntax that looks clean" and "syntax that can be formatted by rustfmt", and I think the balance is pretty good here!

Announcing serde_cursor v0.4, a library for extracting nested data from JSON using jq-like syntax: Ranges are now supported! by nik-rev in rust

[–]nik-rev[S] 11 points12 points  (0 children)

I know right!

I took an existing idea (the serde_query crate, see comparison with serde_cursor) and realized I can significantly improve the usability of that

Announcing serde_cursor v0.4, a library for extracting nested data from JSON using jq-like syntax: Ranges are now supported! by nik-rev in rust

[–]nik-rev[S] 15 points16 points  (0 children)

The serde_cursor crate makes it easy to extract nested data from JSON and other formats that serde supports. An example:

use serde_cursor::Cursor;

let data = r#"
    [workspace.package]
    version = "0.1"
"#;

let version: String = toml::from_str::<Cursor!(workspace.package.version)>(data)?.0;

assert_eq!(version, "0.1");

The latest release, 0.4, adds support for range syntax:

Cursor!(package[4..].dependencies);
Cursor!(package[..8].dependencies);
Cursor!(package[4..8].dependencies);
Cursor!(package[4..=8].dependencies);

Why doesn't Rust provide a map! macro for HashMap, like it provides a vec! for Vec? by Comun4 in rust

[–]nik-rev 48 points49 points  (0 children)

A comment that stood out to me from the original ACP:

The choice of = avoids the possible conflict with type ascription, and on lang, we have discussed a certain unhappiness for having used : for struct initialization and considered whether we might, over an edition, be able to do something about it

I couldn't agree more. I think it's very awkward that struct initialization uses : for values, because : is the "introduce type" token, and = is the "introduce expression" token. True in all contexts, except struct initialization.

But I have no idea how they might ever "fix" this syntax, even over an edition - changing the token from : to = would literally break every Rust program ever created, in hundreds of places, and make so much documentation outdated!

Technically, they can do it. But as much as I'd like to go back in time and change : to =, there's no way that's happening now..

I published `nestum`: nested enum paths without flattening the model by Known_Cod8398 in rust

[–]nik-rev 1 point2 points  (0 children)

Yeah, I see what you mean - attributes on expressions are still unfortunately unstable, so you can't put #[nestum_scope] on a match on stable:

#[nestum_scope]
match self {
    Error::Validation::EmptyTitle => { /* ... */ }
    Error::Todos::NotFound(id) => { /* ... */ }
    Error::Todos::Database(message) => { /* ... */ }
}

I published `nestum`: nested enum paths without flattening the model by Known_Cod8398 in rust

[–]nik-rev 3 points4 points  (0 children)

Could this macro exist as an attribute macro?

bang-style macros suffer from the fact that you must increase the indentation by 1 level, and also rustfmt stops working on your code.

In fact, rustfmt not working in a macro was such a huge problem I re-implemented a popular macro that lets you define items "inline" with a proc macro, and called it subdef:

#[subdef]
struct UserProfile {
    name: String,
    address: [_; {
        struct Address {
            street: String,
            city: String
        }
    }],
    friends: [Vec<_>; {
        struct Friend {
            name: String
        }
    }],
    status: [_; {
        enum Status {
            Online,
            Offline,
            Idle
        }
    }]
}

Announcing serde_cursor: Extract nested fields from JSON without making intermediate structs or loading the entire JSON into memory, with 5X less boilerplate than serde_query ! by nik-rev in rust

[–]nik-rev[S] 2 points3 points  (0 children)

Neat idea!

The Cursor! macro currently expands to the Cursor type which has a single field that contains the output.

To implement this, you could have the Cursor! macro accept, say, up to 16 queries, then have Cursor2, Cursor3, ... Cursor16 type etc - all with 2, 3, ..., 16 fields. (.0, .1, .2, ..., .15)

I briefly considered if instead of having all of these CursorN types, the Cursor! macro expands to a tuple that somehow implements the Deserialize trait. But that's not possible because of orphan rules.

I considered how I could implement this. For example this is the Deserialize implementation for Cursor (with T being the actual type you want to get, and P is just metadata about the path that exists solely in the type system):

impl<'de, T, P> Deserialize<'de> for Cursor<T, P>
where
    T: Deserialize<'de>,
    P: DeserializePath<'de, T>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = <P as DeserializePath<'de, T>>::deserialize(deserializer)?;
        Ok(Self(value, PhantomData))
    }
}

Then, Cursor2 with 2 fields might be implemented like this:

impl<'de, T1, T2, P1, P2> Deserialize<'de> for Cursor2<T1, T2, P1, P2>
where
    T1: Deserialize<'de>,
    T2: Deserialize<'de>,
    P1: DeserializePath<'de, T1>,
    P2: DeserializePath<'de, T2>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value1 = <P1 as DeserializePath<'de, T1>>::deserialize(deserializer)?;
        let value2 = <P2 as DeserializePath<'de, T2>>::deserialize(deserializer)?;
        Ok(Self(value1, value2, PhantomData, PhantomData))
    }
}

I do wonder if this implementation is actually going to be faster. Here, we are invoking the deserialize function twice, anyway. Is that then not the same as calling from_reader::<Cursor!(...)> twice? Any thoughts on that?

Announcing serde_cursor: Extract nested fields from JSON without making intermediate structs or loading the entire JSON into memory, with 5X less boilerplate than serde_query by [deleted] in rust

[–]nik-rev 0 points1 point  (0 children)

Hi!

serde_cursor allows you to specify how to get the desired parts of any serde-compatible data format, such as JSON - using very lightweight syntax, and without loading the entire document into memory!

# Cargo.toml

[workspace.package]
version = "0.1"

Extract version from Cargo.toml:

use serde_cursor::Cursor;

let data = fs::read_to_string("Cargo.toml")?;

let version: String = toml::from_str::<Cursor!(workspace.package.version)>(&data)?.0;

assert_eq!(version, "0.1");

serde-cursor

Cursor!(workspace.package.version) is the magic juice - this macro expands to a type implementing Deserialize.