Skip to content

Add RawValue::from_string_unchecked - #1331

Merged
dtolnay merged 2 commits into
serde-rs:masterfrom
WonderLawrence:rawvalue-from-string-unchecked
Jul 20, 2026
Merged

dtolnay merged 2 commits into
serde-rs:masterfrom
WonderLawrence:rawvalue-from-string-unchecked

Conversation

@WonderLawrence

Copy link
Copy Markdown
Contributor

Add RawValue::from_string_unchecked

An unchecked counterpart of RawValue::from_string, for strings that are already known to be valid JSON — typically the output of another JSON serializer, or JSON that was validated once and cached.

Motivation

RawValue::from_string re-parses the entire input to establish the validity invariant. When the input is trusted by construction, that parse is pure overhead and becomes significant on hot paths that handle large payloads.

Concrete case: JSON-RPC servers (Ethereum execution clients such as reth and its forks) serve multi-megabyte, hot-cached or SIMD-serialized results. The only public way to embed such a payload today is from_string, whose validation scan claws back roughly a third of the serializer's gains — measured on x86_64/AVX2 for a 4 MB payload: serialize with sonic-rs 272 µs, then from_string validation 740 µs. jsonrpsee has the same redundant cost internally on every response it builds from its own serializer output (paritytech/jsonrpsee#1648).

Design

Follows the str::from_utf8 / str::from_utf8_unchecked precedent: the checked API stays the default, and the unsafe fn opts out of validation with the contract documented under # Safety — other code (including unsafe code) is allowed to rely on RawValue containing well-formed JSON, in the same way unsafe code may rely on str containing valid UTF-8.

Additive, no behavior change for existing APIs, gated behind the existing raw_value feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@dtolnay dtolnay left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this looks reasonable.

The "single well-formed JSON value" requirement makes sense but the "no leading or trailing whitespace" requirement might be a bit subtle. I expect many prospective callers might not realize if they have a trailing newline, such as a value coming from an include_str! where most text editors will fight you from trying to save the file without a trailing newline.

I think we should consider one of:

  • Trim whitespace without validating JSON. This is O(1) in the common case of 0 leading whitespace and 0–1 trailing whitespace.
  • Debug-assert the whitespace requirement.
  • Debug-assert both requirements.

Per review, the "no leading or trailing whitespace" clause of the safety
contract is easy to violate accidentally (e.g. a trailing newline from
include_str!). Adding a debug_assert! that re-parses the input and checks
the borrowed slice length catches both requirements at once, and compiles
out in release so the "no re-parse" property still holds.
@WonderLawrence

Copy link
Copy Markdown
Contributor Author

Thanks for the quick review!

I went with your third suggestion — a debug_assert! that covers both requirements at once. The check reuses crate::from_str::<&Self>(&json) and then compares borrowed.json.len() == json.len(); a successful parse gives the "single well-formed JSON value" invariant, and the length equality gives "no leading or trailing whitespace" (mirroring the branch already used in from_string). Release builds compile the assertion out, so the "no re-parse" property advertised in the doc still holds.

Why not option 1 (trim in the library):

  • Leading whitespace can't be trimmed without either allocating a fresh String (defeats the zero-copy motivation) or an in-place drain(..n) (O(n) memmove). The include_str! / trailing-newline case is common, but leading-ws inputs are the ones that would silently regress performance, and there's no way at the API level to signal to the caller which happened.
  • Absorbing whitespace also weakens the invariant that RawValue's bytes are byte-identical to what the caller passed in, which matters for callers who cache the string alongside the RawValue.

Why not option 2 (debug-assert whitespace only): the well-formedness violation is the strictly more severe failure mode (it corrupts the enclosing document, not just adds spaces), so checking only whitespace felt inverted. Doing both is essentially free — the parser walks the input once.

Two #[should_panic] tests were added to lock in the debug-build behavior for each of the two contract clauses.

@dtolnay dtolnay left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@dtolnay
dtolnay merged commit 3b2b3c5 into serde-rs:master Jul 20, 2026
16 checks passed
@dtolnay

dtolnay commented Jul 20, 2026

Copy link
Copy Markdown
Member

Published in 1.0.151.

UneBaguette pushed a commit to vexahub/voprf-vx that referenced this pull request Aug 5, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [displaydoc](https://github.com/yaahc/displaydoc) | dependencies | patch | `0.2.6` → `0.2.7` |
| [hybrid-array](https://github.com/RustCrypto/hybrid-array) | dependencies | patch | `0.4.13` → `0.4.14` |
| [regex](https://github.com/rust-lang/regex) | dev-dependencies | patch | `1.13.0` → `1.13.1` |
| [serde](https://serde.rs) ([source](https://github.com/serde-rs/serde)) | dependencies | patch | `1.0.228` → `1.0.229` |
| [serde_json](https://github.com/serde-rs/json) | dev-dependencies | patch | `1.0.150` → `1.0.151` |

---

### Release Notes

<details>
<summary>yaahc/displaydoc (displaydoc)</summary>

### [`v0.2.7`](https://github.com/yaahc/displaydoc/blob/HEAD/CHANGELOG.md#027---2026-07-28)

[Compare Source](yaahc/displaydoc@v0.2.6...v0.2.7)

#### Added

- Updated `syn` dependency to 3.0

#### Changed

- Bumped MSRV to 1.71

</details>

<details>
<summary>RustCrypto/hybrid-array (hybrid-array)</summary>

### [`v0.4.14`](https://github.com/RustCrypto/hybrid-array/blob/HEAD/CHANGELOG.md#0414-2026-07-30)

[Compare Source](RustCrypto/hybrid-array@v0.4.13...v0.4.14)

##### Added

- Sizes for `sntrup` ([#&#8203;231])

[#&#8203;231]: RustCrypto/hybrid-array#231

</details>

<details>
<summary>rust-lang/regex (regex)</summary>

### [`v1.13.1`](https://github.com/rust-lang/regex/blob/HEAD/CHANGELOG.md#1131-2026-07-15)

[Compare Source](rust-lang/regex@1.13.0...1.13.1)

\===================
This is a release that fixes a bug where incorrect regex match offsets could be
reported. Note that this doesn't impact whether a match occurs or not, just
where it occurs. The match offsets are still valid for slicing, they just may
not refer to the correct leftmost-first match. See
[#&#8203;1364](rust-lang/regex#1364) for (many) more details.

Bug fixes:

- [#&#8203;1354](rust-lang/regex#1354):
  Fixes previously unsound reverse suffix and inner optimizations.

</details>

<details>
<summary>serde-rs/serde (serde)</summary>

### [`v1.0.229`](https://github.com/serde-rs/serde/releases/tag/v1.0.229)

[Compare Source](serde-rs/serde@v1.0.228...v1.0.229)

- Update to syn 3

</details>

<details>
<summary>serde-rs/json (serde_json)</summary>

### [`v1.0.151`](https://github.com/serde-rs/json/releases/tag/v1.0.151)

[Compare Source](serde-rs/json@v1.0.150...v1.0.151)

- Add RawValue::from\_string\_unchecked ([#&#8203;1331](serde-rs/json#1331), thanks [@&#8203;WonderLawrence](https://github.com/WonderLawrence))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODEuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjExLjYiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://dev.unebaguette.fr/vexahub/voprf-vx/pulls/16
Co-authored-by: Renovate Bot <renovatebot@lunomail.com>
TW0hank0 added a commit to TW0hank0/positive_mahjong that referenced this pull request Aug 19, 2026
Update patch dependencies

This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [console_error_panic_hook](https://github.com/rustwasm/console_error_panic_hook) | workspace.dependencies | patch | `=0.1.5` → `=0.1.7` |
| [godot](https://godot-rust.github.io) ([source](https://github.com/godot-rust/gdext)) | workspace.dependencies | patch | `=0.5.3` → `=0.5.5` |
| [serde](https://serde.rs) ([source](https://github.com/serde-rs/serde)) | workspace.dependencies | patch | `=1.0.228` → `=1.0.229` |
| [serde_json](https://github.com/serde-rs/json) | workspace.dependencies | patch | `=1.0.149` → `=1.0.151` |

---

### Release Notes

<details>
<summary>rustwasm/console_error_panic_hook (console_error_panic_hook)</summary>

### [`v0.1.6`](rustwasm/console_error_panic_hook@0.1.5...0.1.6)

[Compare Source](rustwasm/console_error_panic_hook@0.1.5...0.1.6)

</details>

<details>
<summary>godot-rust/gdext (godot)</summary>

### [`v0.5.5`](https://github.com/godot-rust/gdext/blob/HEAD/Changelog.md#v055)

[Compare Source](godot-rust/gdext@v0.5.4...v0.5.5)

*9 August 2026*

##### 🌻 Features

- `EngineBitfield`: add `with()` and `without()` methods ([#&#8203;1627](godot-rust/gdext#1627))
- `#[func(virtual)]` now supports `async fn` for GDScript coroutines ([#&#8203;1645](godot-rust/gdext#1645))
- Add `PhantomData<T>` support for `GodotConvert` derive macro ([#&#8203;1619](godot-rust/gdext#1619))

##### 📈 Performance

- Singletons are now cached ([#&#8203;1636](godot-rust/gdext#1636))
- Cache user singleton pointers ([#&#8203;1644](godot-rust/gdext#1644))
- Cheaper refcount operations ([#&#8203;1669](godot-rust/gdext#1669))

##### 🧹 Quality of life

- Always use godot\_error in panic hook ([#&#8203;1647](godot-rust/gdext#1647))
- Rename `cast_int/cast_float` to `to_vector*/to_rect*` ([#&#8203;1661](godot-rust/gdext#1661))
- Validate class registrations against `ClassDB` ([#&#8203;1664](godot-rust/gdext#1664))

##### 🛠️ Bugfixes

- Fix `#[var(pub)]` panic during `OnEditor<Gd<T>>` reload ([#&#8203;1639](godot-rust/gdext#1639))
- Fix async `SignalFuture` panic when object is freed before exit flag ([#&#8203;1648](godot-rust/gdext#1648))
- Fix `api-custom` build when prebuilt lags detected Godot version ([#&#8203;1649](godot-rust/gdext#1649))
- Harden various parts of codebase ([#&#8203;1660](godot-rust/gdext#1660))
- Fix `GFile` `BufRead` past EOF ([#&#8203;1670](godot-rust/gdext#1670))

##### 🏗️ Maintenance

- Replace `godot-cell` borrow-state proptests with exhaustive model check ([#&#8203;1650](godot-rust/gdext#1650))
- Make accidental access of `#[doc(hidden)]` symbols more explicit ([#&#8203;1651](godot-rust/gdext#1651))
- Follow-up to rustc / Godot changes ([#&#8203;1659](godot-rust/gdext#1659))
- Name virtual-callback layers after user-facing `I*` virtuals ([#&#8203;1662](godot-rust/gdext#1662))
- Move ref-counting operations from `DynMemory` to `RawGd` ([#&#8203;1667](godot-rust/gdext#1667))

### [`v0.5.4`](https://github.com/godot-rust/gdext/blob/HEAD/Changelog.md#v054)

[Compare Source](godot-rust/gdext@v0.5.3...v0.5.4)

*23 June 2026*

##### 🌻 Features

- Godot 4.7: add `api-4-7` level ([#&#8203;1641](godot-rust/gdext#1641))
- Type-Safe API for User-defined RPCs ([#&#8203;1535](godot-rust/gdext#1535), [#&#8203;1634](godot-rust/gdext#1634))
- String conversions: ` to_gstring()`, `to_string_name()` etc ([#&#8203;1623](godot-rust/gdext#1623))
- Thread-safe FFI scaffolding + cross-thread printing ([#&#8203;1632](godot-rust/gdext#1632))
- `Gd` introspection APIs: `dynamic_class`, `is_dynamic_class[_of]`, `is_ref_counted` ([#&#8203;1631](godot-rust/gdext#1631))
- Support async versions of `tools::load()` and `tools::try_load()` ([#&#8203;1601](godot-rust/gdext#1601))
- Support `@export_node_path` attribute ([#&#8203;1620](godot-rust/gdext#1620))

##### 🧹 Quality of life

- Slightly more flexible `GodotStringExt` through unsized impls ([#&#8203;1629](godot-rust/gdext#1629))
- Suppress Godot errors in fallible functions ([#&#8203;1637](godot-rust/gdext#1637))

##### 🛠️ Bugfixes

- Fix Linux hot-reload panic when TLS destructors fire before init ([#&#8203;1611](godot-rust/gdext#1611))
- Fix panic-in-drop in future tests; harden against race conditions ([#&#8203;1617](godot-rust/gdext#1617))
- Auto-remove custom callables also when connected in untyped APIs ([#&#8203;1612](godot-rust/gdext#1612))
- Fix async panics at shutdown ([#&#8203;1625](godot-rust/gdext#1625))
- Fix `Object` returns with dynamic type `RefCounted` ([#&#8203;1628](godot-rust/gdext#1628))
- Fix memory leak for `connect_self()` on `RefCounted` ([#&#8203;1635](godot-rust/gdext#1635))
- Fix dangling singleton pointer after deinit ([#&#8203;1638](godot-rust/gdext#1638))
- Fix RPC borrow panic for `call_local` ([#&#8203;1643](godot-rust/gdext#1643))

##### 🏗️ Maintenance

- Add `once` flag to `defer_startup_warn!` and `defer_startup_error!` ([#&#8203;1613](godot-rust/gdext#1613))
- CI: unzip occasionally fails; retry ([#&#8203;1616](godot-rust/gdext#1616))
- Rename self-registration parts: `plugin` -> `shard` ([#&#8203;1618](godot-rust/gdext#1618))
- Replace internal `PassiveGd` with safer `BorrowedGd` ([#&#8203;1633](godot-rust/gdext#1633))

##### 📚 Documentation

- Document `register-docs` security; warn when used in export builds ([#&#8203;1615](godot-rust/gdext#1615))
- Document `#[class(tool)]` + `ready()` behavior across editor reload ([#&#8203;1614](godot-rust/gdext#1614))
- Fix borrow panic in `godot::task::spawn` doc example ([#&#8203;1622](godot-rust/gdext#1622))

</details>

<details>
<summary>serde-rs/serde (serde)</summary>

### [`v1.0.229`](https://github.com/serde-rs/serde/releases/tag/v1.0.229)

[Compare Source](serde-rs/serde@v1.0.228...v1.0.229)

- Update to syn 3

</details>

<details>
<summary>serde-rs/json (serde_json)</summary>

### [`v1.0.151`](https://github.com/serde-rs/json/releases/tag/v1.0.151)

[Compare Source](serde-rs/json@v1.0.150...v1.0.151)

- Add RawValue::from\_string\_unchecked ([#&#8203;1331](serde-rs/json#1331), thanks [@&#8203;WonderLawrence](https://github.com/WonderLawrence))

### [`v1.0.150`](https://github.com/serde-rs/json/releases/tag/v1.0.150)

[Compare Source](serde-rs/json@v1.0.149...v1.0.150)

- Reject non-string enum object keys ([#&#8203;1324](serde-rs/json#1324), thanks [@&#8203;puneetdixit200](https://github.com/puneetdixit200))

</details>

---

### Configuration

📅 **Schedule**: (in timezone Asia/Taipei)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This MR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zMy4yIiwidXBkYXRlZEluVmVyIjoiNDQuMzMuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

See merge request TW0hank0/positive_mahjong!142
ldicarlo pushed a commit to legifrss/legifrss that referenced this pull request Aug 25, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_json](https://github.com/serde-rs/json) | dependencies | patch | `1.0.150` → `1.0.151` |

---

### Release Notes

<details>
<summary>serde-rs/json (serde_json)</summary>

### [`v1.0.151`](https://github.com/serde-rs/json/releases/tag/v1.0.151)

[Compare Source](serde-rs/json@v1.0.150...v1.0.151)

- Add RawValue::from\_string\_unchecked ([#&#8203;1331](serde-rs/json#1331), thanks [@&#8203;WonderLawrence](https://github.com/WonderLawrence))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE3MS4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate <renovate@impure.systems>
Co-authored-by: Luca Di Carlo <luca@noreply.git.impure.systems>
Reviewed-on: https://git.impure.systems/luca/legifrss/pulls/18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants