Skip to content

feat!: Use a radix trie for route matching - #628

Open
ElijahAhianyo wants to merge 22 commits into
masterfrom
elijah/router-trie
Open

feat!: Use a radix trie for route matching#628
ElijahAhianyo wants to merge 22 commits into
masterfrom
elijah/router-trie

Conversation

@ElijahAhianyo

@ElijahAhianyo ElijahAhianyo commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Description

The current router implementation uses a Vec to store routers, which has some real limitations. For example, using a vec meant route conflict detection was cumbersome and hacky to get right. Using a Radix trie is the right data structure for this problem. This PR delegates the core Trie logic to the matchit crate. We still keep our business logic in a light wrapper over the Matchit Router.

Breaking Changes

  1. Registering a route with a duplicate parameter at the same segment will now fail
Router::with_urls([
    Route::with_handler_and_name("/foo/{bar}", index, "index"),
        Route::with_handler_and_name("/foo/{baz}", add_example_form, "add"),
])
  1. Registering 2 views with the same url_prefix will fail
struct TestProject;
impl Project for TestProject {
   ...
    fn register_apps(&self, apps: &mut AppBuilder, _context: &RegisterAppsContext) {
        apps.register_with_views(App1, "");
        apps.register_with_views(App2, "");
    }
}
  1. When a handler and router are registered for the same route, the handler will take precedence at lookup time
let nested_router = Router::with_urls([Route::with_handler_and_name(
        "/inner/{id}",
        nested,
       "nested",
)]);

Router::with_urls([
    Route::with_handler_and_name("/foo", index, "index"),
    Route::with_router("/foo", nested_router),
])
  1. A route is treated as different from its trailing-slash counterpart:
Router::with_urls([
    Route::with_handler_and_name("/foo", index, "index"),
    Route::with_handler_and_name("/foo/", another_handler, "another" ),
])
  1. This does not apply to routers. The example below is invalid and will fail:
Router::with_urls([
    Route::with_router("/foo", nested_router_a),
    Route::with_router("/foo/", nested_router_b ),
])

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / cleanup
  • Performance improvement
  • Other (describe above)

@github-actions github-actions Bot added the C-lib Crate: cot (main library crate) label Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Projectcot
Branchelijah/router-trie
Testbedgithub-ubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
empty_router/empty_router📈 view plot
🚷 view threshold
14.97 ms
(+60.08%)Baseline: 9.35 ms
17.77 ms
(84.22%)
json_api/json_api📈 view plot
🚷 view threshold
1.14 ms
(+10.88%)Baseline: 1.03 ms
1.37 ms
(83.69%)
nested_routers/nested_routers📈 view plot
🚷 view threshold
1.10 ms
(+14.24%)Baseline: 0.97 ms
1.26 ms
(87.70%)
single_root_route/single_root_route📈 view plot
🚷 view threshold
1.03 ms
(+11.18%)Baseline: 0.93 ms
1.22 ms
(84.68%)
single_root_route_burst/single_root_route_burst📈 view plot
🚷 view threshold
19.51 ms
(+15.89%)Baseline: 16.84 ms
21.47 ms
(90.89%)
🐰 View full continuous benchmarking report in Bencher

@github-actions github-actions Bot added the A-deps Area: Dependencies label Aug 17, 2026
@ElijahAhianyo

Copy link
Copy Markdown
Contributor Author

Should be rebased on and merged after #586

@ElijahAhianyo ElijahAhianyo changed the title Use a radix trie for route matching feat!: Use a radix trie for route matching Aug 18, 2026
@ElijahAhianyo
ElijahAhianyo marked this pull request as ready for review August 25, 2026 04:22
@ElijahAhianyo
ElijahAhianyo requested a review from a team August 25, 2026 20:30
Comment thread cot/tests/project.rs
fn register_apps(&self, apps: &mut AppBuilder, _context: &RegisterAppsContext) {
apps.register_with_views(App1, "");
apps.register_with_views(App2, "");
apps.register_with_views(App2, "/foo");

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.

Do we have any way of registering multiple routers at the same URL? This is an important feature - people might want to merge different routers at the same base URL.

If matchit doesn't let us do that because it detects a conflict, we should merge the routers. I'm not sure if we should have separate methods to do that explicitly, or keep the implicit behavior - I'll let you decide.

Comment thread cot/src/router/path.rs Outdated
Comment thread cot/src/router.rs Outdated
/// Panics when a url string could not be parsed into a [`Route`]
#[must_use]
pub fn with_urls<T: Into<Vec<Route>>>(urls: T) -> Self {
match Self::try_with_urls(urls) {

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.

Why not just use expect here?

Comment thread cot/tests/admin.rs Outdated
Comment thread cot/src/openapi.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-deps Area: Dependencies C-lib Crate: cot (main library crate)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants