· 6 min read ·

Searching Rust by Type: What Roogle Gets Right About API Discovery

Source: lobsters

When you know what a function should do but not what it’s called, name-based search fails you. This is the core problem that Roogle is trying to solve for the Rust ecosystem: a search engine that lets you query the standard library and crates by type signature, the way Haskell developers have searched their ecosystem with Hoogle for decades.

The idea is simple to state. If you want a function that transforms an Option<T> into a T by providing a fallback, you search for something like Option<T>, T -> T. Roogle returns Option::unwrap_or. If you want to know what functions take a Vec<T> and give back a single value, you search that shape. You don’t need to remember the name fold or reduce or whatever a particular crate author decided to call it.

This is how Hoogle has worked since 2004 in Haskell-land. You type (a -> b) -> [a] -> [b] and it returns map. You type [a] -> Int -> a and it returns (!!). The type system carries enough semantic information that the signature alone is often sufficient to identify exactly what you need. Roogle is a direct translation of that idea into Rust.

Why Name Search Breaks Down

The current state of Rust API search is docs.rs combined with whatever your editor’s LSP can surface, and both are fundamentally name-oriented. You can grep through documentation, search by crate name, filter by module. What you cannot do is say “give me everything with this input-output shape” across the whole ecosystem.

This matters more than it might seem. Rust’s standard library alone has hundreds of methods on core types, many of them subtle variations on a theme. The Iterator trait has over 70 methods. When you’re writing a data pipeline and need to know whether there’s a built-in method that does zip followed by map in one step, or whether you need to chain them manually, you’re stuck either memorizing the entire surface area or doing approximate keyword searches and hoping.

Beyond the stdlib, the crate ecosystem is enormous and inconsistently named. A function that converts one type to another might be called into, from, convert, cast, coerce, or something domain-specific. Type-directed search cuts through naming inconsistency because two functions with the same type signature are functionally equivalent up to the semantics encoded in their names.

How Roogle Works

Roogle builds its index from rustdoc’s JSON output format, which has been available on nightly since around 2021. The JSON format exposes the complete type information of every public item in a crate: function signatures, struct fields, trait implementations, associated types, the whole structure. This is the same data that powers docs.rs rendering, but in a machine-readable form that’s much easier to work with programmatically than parsing HTML.

When you submit a query, Roogle parses it into an internal type representation and then runs a unification pass against the indexed signatures. Type unification here means treating the free type variables in your query (the T and U placeholders) as wildcards that can match against concrete types in the index. A query for Vec<T> -> usize matches Vec::len because T can be unified with whatever concrete type the vector holds.

The matching isn’t exact in the strict type-theoretic sense, which is actually the right call for a search tool. Strict unification would miss useful results due to minor structural differences, lifetime annotations that don’t affect behavior, or impl Trait vs concrete type distinctions. Roogle does fuzzy matching that prioritizes structural similarity while still ranking more precise matches higher.

Where Rust Makes This Hard

Hoogle has it relatively easy. Haskell’s type system is expressive but follows fairly regular rules, and the ecosystem converges on a standard set of type class abstractions that make signatures predictably comparable. Rust’s type system is more complex in ways that directly affect type-directed search.

Lifetimes are the obvious complication. A function with signature fn foo<'a>(s: &'a str) -> &'a str is structurally different from fn foo(s: &str) -> &str in the type metadata, even though they express the same constraint. A search for &str -> &str should ideally match both. Roogle has to make decisions about how aggressively to elide lifetime information in queries versus preserving it for more specific searches.

Trait bounds add another layer. fn sum<T: Add<Output = T>>(items: Vec<T>) -> T has a very different set of valid callers than fn identity<T>(x: T) -> T, even though both have the surface shape Vec<T> -> T and T -> T respectively. Surfacing the trait constraints in search results matters for understanding what you can actually call a function with.

Associated types complicate things further. Iterator::Item is not a free type variable in the usual sense; it’s determined by whatever concrete type implements Iterator. A search for Iterator -> Vec<T> needs to understand that the T in Vec<T> corresponds to Self::Item from the iterator’s associated type, not an independent variable.

And then there’s impl Trait, which can appear in both argument and return position, hiding concrete types behind trait bounds in ways that make signature comparison non-trivial.

None of these are unsolvable problems, but each one requires explicit design decisions. Roogle is early enough that some of these cases are still being worked through.

Comparing to the Broader Ecosystem

OCaml has had Sherlodoc and earlier tools like odig that support doc search. The OCaml community has also invested in Merlin, which provides type-directed completion at the editor level, which partially overlaps with what Roogle does at the search level.

Go has no equivalent, and given its intentionally constrained type system, the need is less acute. There are only so many ways to write a func([]T) T in Go before you’ve enumerated them.

Haskell’s Hoogle remains the gold standard. It’s been running for twenty years, covers essentially the entire Hackage ecosystem, and is fast enough for interactive use. The Hoogle source is instructive reading if you’re building anything in this space: the indexing strategy, the ranking heuristics, and the query parser all reflect hard-won lessons about what developers actually search for.

Rust’s cargo doc and docs.rs have gradually improved their search UX, and there’s been ongoing discussion in the Rust project about adding type-based search directly to rustdoc. Roogle is essentially a proof of concept and pressure test for what that would look like.

What It Reveals About Tooling Maturity

The existence of Roogle as an independent project points to something real about where Rust tooling is in its maturity curve. The language itself is stable and expressive. The build toolchain with Cargo is excellent. Documentation generation is good. But the tooling for navigating and discovering the surface area of a large, type-rich codebase is still catching up.

Type-directed search is not a niche academic feature. It’s a practical tool for anyone working with a large type-rich API surface, which describes most serious Rust codebases. The fact that it requires an independent project rather than shipping as part of rustdoc or docs.rs is a gap worth noting.

Roogle is still actively developed and not yet comprehensive in its crate coverage or its handling of all Rust type constructs. But the direction is right. If you’re building anything in the Rust ecosystem and find yourself scrolling through Iterator methods trying to remember which one does what you need, it’s worth knowing this tool exists and pointing people toward it. Type-search is one of those features that, once you’ve used it in Haskell, makes every other language’s documentation feel incomplete by comparison.

The Roogle repository includes a hosted demo and instructions for running it locally against your own crates. For anyone spending serious time in Rust, it’s worth having in the toolbox.

Was this interesting?