<!--
title: API design patterns
domain: runtimes
topic: API
language: Rust
source: servo/servo
updated: 2025-10-09
url: https://awesomereviewers.com/reviewers/servo-api-design-patterns/
-->

Design APIs with appropriate parameter types, place conversion functions as trait implementations in the correct modules, and reuse existing patterns instead of creating new abstractions.

Key principles:
1. **Use appropriate parameter types**: Prefer `&[T]` over `&Vec<T>` for function parameters to accept more input types and improve API flexibility.

2. **Place conversions as trait implementations**: When creating conversion functions, implement them as traits (like `From<&str>`) in the module where the target type is defined, rather than standalone functions elsewhere.

3. **Reuse existing patterns**: Before creating new IPC messages, traits, or abstractions, check if existing patterns can be extended or reused. Avoid adding dependencies just for cleaner conversions when simple functions suffice.

4. **Prefer functional patterns**: Use `map()`, `unwrap_or_else()`, and similar combinators instead of imperative if-let chains when it improves readability.

Example of good API design:
```rust
// Good: slice parameter, functional style
fn process_link_headers(link_headers: &[LinkHeader]) -> Vec<ProcessedLink> {
    link_headers.iter()
        .map(|header| process_header(header))
        .collect()
}

// Good: trait implementation in type's module
impl From<&str> for CorsSettings {
    fn from(token: &str) -> Self {
        match_ignore_ascii_case! { token,
            "anonymous" => CorsSettings::Anonymous,
            "use-credentials" => CorsSettings::UseCredentials,
            _ => CorsSettings::Anonymous,
        }
    }
}
```

This approach creates more flexible, discoverable, and maintainable APIs while avoiding unnecessary complexity.
