Skip to content

feat(path): deserialize wildcard captures into Vec by splitting on / - #3828

Open
patelshudhanshu1999-maker wants to merge 1 commit into
tokio-rs:mainfrom
patelshudhanshu1999-maker:fix/path-wildcard-vec
Open

feat(path): deserialize wildcard captures into Vec by splitting on /#3828
patelshudhanshu1999-maker wants to merge 1 commit into
tokio-rs:mainfrom
patelshudhanshu1999-maker:fix/path-wildcard-vec

Conversation

@patelshudhanshu1999-maker

@patelshudhanshu1999-maker patelshudhanshu1999-maker commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #3621

Motivation

When a route uses a wildcard capture like /{*path}, there's no ergonomic way to extract the individual path segments. A Path<Vec<String>> deserializes a single capture into a one-element vector containing the entire matched string, and using a Vec<T> inside a struct field (e.g., struct Params { key: Vec<String> }) fails with an unsupported-type rejection.

Users working with file-system-like routes (/files/{*path}) or S3-style APIs (/{bucket}/{*key}) need to manually split the captured string, which is boilerplate-heavy and error-prone.

Solution

When a route has a single path parameter (typically a wildcard), the Path extractor now splits the captured value on / and deserializes each non-empty segment individually into the target sequence type. This works for:

  • Top-level: Path<Vec<String>>, Path<Vec<i32>>, etc.
  • Struct fields: struct Params { bucket: String, key: Vec<String> }
  • Tuple elements: (String, Vec<String>)

Key design decisions

  • Empty segments are skipped — trailing slashes and double slashes don't produce empty strings in the output.
  • Vec<(String, String)> is unaffected — it continues to deserialize into (name, value) pairs of all captures, preserving backward compatibility.
  • Percent-decoding happens before splitting — since matchit delivers percent-decoded values, an encoded / (%2F) also acts as a separator.
  • All-empty segments edge case — when the value has only empty segments (e.g., /), the behavior depends on context: in a struct field the result is an empty Vec, while at the top level the original single-element behavior is preserved.

When a route has a single path parameter (typically a wildcard like
`{*path}`), the `Path` extractor can now deserialize it into a
`Vec<T>` by splitting the captured value on `/` and skipping empty
segments. This works for:

- Top-level `Path<Vec<String>>` and `Path<Vec<T>>`
- Struct fields: `struct Params { key: Vec<String> }`
- Tuple elements: `(String, Vec<String>)`

Previously a top-level `Vec` deserialized a single capture into one
element, and struct/tuple `Vec` fields failed with an unsupported-type
error.

Fixes tokio-rs#3621

@Samielakkad Samielakkad left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is an inconsistency in the all-empty-segment case for a single capture.

The docs say sequence deserialization splits the decoded capture on / and skips empty segments, and the struct-field test follows that behavior:

struct Params { key: Vec<String> }
// key = "/" => key: vec![]

But top-level Path<Vec<String>> currently falls back to the old ?one capture becomes one element? path when the split has no non-empty segment:

// value = "/" or "//" => vec!["/".to_owned()] / vec!["//".to_owned()]

That comes from PathDeserializer::deserialize_seq: it only constructs SingleParamSeqAccess after finding a non-empty first segment, otherwise it falls through to SeqDeserializer.

This can show up with a decoded %2F / %2F%2F capture, and it means Path<Vec<T>> and Path<StructWithVec> disagree for the same captured value. I think the top-level sequence path should commit to split-mode whenever there is exactly one capture, even if the resulting segment list is empty, or the docs should explicitly call out the fallback behavior.

@mladedav

Copy link
Copy Markdown
Collaborator

Why was previous work in #3680 not pointed out and what complexity is this solving on top of that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Path extractor Vec<String> deserialization support for wildcard paths

3 participants