La documentación sirve como puente entre los desarrolladores y los usuarios de su código. En el ecosistema de Rust, la documentación se eleva a un ciudadano de primera clase a través de Rustdoc, una sofisticada herramienta de generación de documentación que se incluye con la distribución estándar de Rust. A diferencia de las herramientas de documentación en muchos otros lenguajes, Rustdoc no se limita a generar documentación estática, sino que crea sitios web de documentación interactivos, comprobables y con un formato enriquecido que mejoran la capacidad de descubrimiento y la usabilidad del código.
Para los desarrolladores de Rust, Apidog ofrece funciones integrales de documentación de API para las API HTTP con capacidades de prueba interactivas, formato de respuesta visual y funciones de colaboración.

Apidog se centra en la documentación de endpoints, formatos de solicitud/respuesta y especificaciones HTTP, Rustdoc se especializa en la documentación del código a nivel de lenguaje, documentando structs, funciones, traits y otras construcciones de programación que componen un crate de Rust. Ambos sistemas comparten el objetivo fundamental de hacer que los sistemas técnicos complejos sean más accesibles a través de una documentación exhaustiva, precisa y utilizable.

¿Qué es Rustdoc?
Rustdoc es una herramienta de línea de comandos que analiza el código fuente de Rust y los comentarios especiales de documentación para generar archivos HTML, CSS y JavaScript que forman un sitio web de documentación navegable. En esencia, Rustdoc opera extrayendo comentarios de documentación de su código y transformándolos en documentación estructurada.
La operación básica de Rustdoc implica:
- Analizar archivos fuente de Rust para extraer comentarios de documentación
- Convertir esos comentarios de Markdown a HTML
- Generar una estructura de sitio web navegable y con capacidad de búsqueda
- Extraer ejemplos de código de la documentación y prepararlos para las pruebas
- Crear referencias cruzadas entre elementos
- Producir activos estáticos para la documentación final
Cuando se invoca directamente, el binario de Rustdoc acepta un archivo fuente de Rust como entrada:
$ rustdoc src/lib.rs --crate-name my_crate
Este comando procesa el archivo lib.rs
y genera la documentación en un directorio doc
de forma predeterminada. La documentación está estructurada jerárquicamente, reflejando la estructura de su código, con páginas separadas para módulos, structs, enums, traits y otras construcciones de Rust.
Internamente, Rustdoc aprovecha las API internas del compilador de Rust para analizar y comprender su código. Esta estrecha integración con el compilador permite a Rustdoc generar referencias cruzadas precisas, documentar correctamente las firmas de tipo y verificar que los ejemplos de código realmente se compilen y ejecuten correctamente.
Comentarios de documentación en Rust
La documentación en Rust se basa en una sintaxis de comentario especial que difiere de los comentarios de código regulares. Hay dos tipos principales de comentarios de documentación:
Comentarios de documentación externos (///
)
Los comentarios de documentación externos documentan el elemento que los sigue y se denotan con tres barras diagonales:
/// Este es un comentario de documentación para la función a continuación.
/// Puede abarcar varias líneas y admite el formato Markdown.
pub fn documented_function() -> bool {
true
}
Estos comentarios describen funciones, structs, enums, traits, módulos y otros elementos en su base de código. Se les llama documentación "externa" porque existen fuera del elemento que documentan.
Comentarios de documentación internos (//!
)
Los comentarios de documentación internos documentan el elemento dentro del cual aparecen y se denotan con //!
:
//! Este módulo proporciona utilidades para analizar archivos de configuración.
//!
//! # Ejemplos
//!
//! ```
//! let config = my_crate::config::parse("config.toml");
//! assert!(config.is_ok());
//! ```
pub fn parse(file_path: &str) -> Result<Config, ParseError> {
// Implementación aquí
}
Los comentarios de documentación internos se utilizan comúnmente para la documentación a nivel de módulo o a nivel de crate. Cuando se colocan al principio de un archivo lib.rs
, documentan todo el crate y forman la página principal de su documentación generada.
La diferencia técnica entre estos estilos de comentario es sutil pero importante: ///
documenta lo que viene después, mientras que //!
documenta lo que lo contiene.
Soporte de Markdown en Rustdoc
Rustdoc utiliza un analizador de Markdown compatible con CommonMark con varias extensiones. Esto brinda a los autores de documentación acceso a un rico conjunto de opciones de formato:
Formato básico
/// # Encabezado de nivel 1
/// ## Encabezado de nivel 2
///
/// Los párrafos están separados por líneas en blanco.
///
/// Se admiten *texto en cursiva* y **texto en negrita**.
///
/// - Listas no ordenadas
/// - Se pueden crear
/// - Así
///
/// 1. Listas ordenadas
/// 2. También funcionan
///
/// El `código en línea` está rodeado de comillas inversas.
Bloques de código
Los bloques de código son particularmente importantes en Rustdoc, ya que cumplen una doble función: muestran ejemplos de código en la documentación y se pueden extraer como pruebas.
/// ```rust
/// // Este es un bloque de código
/// let x = 5;
/// assert_eq!(x, 5);
/// ```
De forma predeterminada, si no se especifica ningún lenguaje después de las comillas inversas triples de apertura, Rustdoc asume que el bloque de código contiene código Rust. Sin embargo, puede especificar otros lenguajes para el resaltado de sintaxis:
/// ```json
/// {
/// "name": "example",
/// "version": "1.0.0"
/// }
/// ```
Extensiones de Rustdoc
Rustdoc extiende CommonMark con varias características adicionales:
Texto tachado
/// El ~~texto tachado~~ usa tildes.
Notas al pie
/// Esta declaración necesita aclaración[^1].
///
/// [^1]: Aquí está la aclaración.
Tablas
/// | Encabezado1 | Encabezado2 |
/// |---------|---------|
/// | celda1 | celda2 |
/// | celda3 | celda4 |
Listas de tareas
/// - [x] Tarea completada
/// - [ ] Tarea incompleta
Puntuación inteligente
Rustdoc convierte automáticamente las secuencias de puntuación ASCII en sus equivalentes Unicode:
--
se convierte en guión corto (–)---
se convierte en guión largo (—)...
se convierte en puntos suspensivos (…)- Las comillas rectas se convierten en comillas curvas
Interfaz de línea de comandos detallada de Rustdoc
Rustdoc ofrece un conjunto completo de opciones de línea de comandos para personalizar la generación de documentación:
$ rustdoc --help
Algunas de las opciones técnicamente más significativas incluyen:
--document-private-items
: De forma predeterminada, Rustdoc solo documenta los elementos públicos. Este indicador incluye elementos privados en la documentación, útil para la documentación interna.--test
: Ejecute ejemplos de documentación como pruebas, verificando que se compilen y ejecuten como se espera.--test-args
: Pase argumentos adicionales al ejecutor de pruebas, como--nocapture
para mostrar la salida de las pruebas.--edition=EDITION
: Especifique la edición de Rust para analizar y ejecutar el código (2015, 2018, 2021, 2024).--target=TARGET
: Genere documentación para la plataforma de destino especificada.--crate-type=TYPE
: Especifique el tipo de crate para las pruebas (bin, lib, rlib, dylib, cdylib, staticlib, proc-macro).-L FLAG=PATH
: Agregue un directorio a la ruta de búsqueda de la biblioteca, crucial para resolver dependencias en las pruebas.--cfg=SPEC
: Pase un indicador--cfg
al compilador, habilitando la compilación condicional en el código de documentación.--extern NAME=PATH
: Especifique la ubicación de un crate externo, permitiendo que las pruebas hagan referencia a dependencias externas.
Para un proyecto con dependencias externas, una invocación típica de Rustdoc podría verse así:
$ rustdoc src/lib.rs --crate-name example_crate \
--edition=2021 \
-L dependency=target/debug/deps \
--extern serde=target/debug/deps/libserde-abcd1234.rlib \
--extern tokio=target/debug/deps/libtokio-efgh5678.rlib
Afortunadamente, Cargo automatiza este complejo proceso con un simple comando:
$ cargo doc --document-private-items
Integración con Cargo
Cargo proporciona una interfaz optimizada para trabajar con Rustdoc a través del comando cargo doc
. Internamente, Cargo llama a Rustdoc con los parámetros apropiados:
$ cargo doc --verbose
Ejecutar este comando mostrará la invocación real de Rustdoc, revelando cómo Cargo configura las rutas a las dependencias y los directorios de salida.
La funcionalidad principal incluye:
cargo doc
: Genere documentación para el crate actual y sus dependenciascargo doc --no-deps
: Genere documentación solo para el crate actualcargo doc --open
: Genere documentación y ábrala en un navegador webcargo doc --document-private-items
: Incluya elementos privados en la documentacióncargo test --doc
: Ejecute pruebas de documentación
Cargo determina de forma inteligente el nombre del crate de su archivo Cargo.toml
, configura la estructura de directorio de salida correcta en target/doc/
y garantiza que todas las dependencias estén vinculadas correctamente.
Estructura y organización de la documentación
Documentación a nivel de crate
La documentación a nivel de crate sirve como página de inicio para su biblioteca y debe proporcionar una descripción general completa. Esto se escribe como documentación interna (//!
) en la parte superior del archivo lib.rs
:
//! # Mi biblioteca avanzada de criptografía
//!
//! Este crate proporciona primitivas criptográficas con un enfoque en:
//!
//! - **Rendimiento**: Implementaciones optimizadas para CPU modernas
//! - **Seguridad**: Algoritmos formalmente verificados
//! - **Usabilidad**: API de alto nivel y difíciles de usar incorrectamente
//!
//! ## Inicio rápido
//!
//! ```rust
//! use crypto_lib::{Cipher, Mode};
//!
//! let key = crypto_lib::generate_key(256);
//! let cipher = Cipher::new(&key, Mode::GCM);
//!
//! let plaintext = b"Mensaje secreto";
//! let ciphertext = cipher.encrypt(plaintext);
//! ```
//!
//! ## Características
//!
//! El crate admite los siguientes algoritmos:
//!
//! - AES (128, 192, 256)
//! - ChaCha20
//! - Poly1305
//! - HMAC
//! - PBKDF2
La documentación eficaz a nivel de crate a menudo incluye:
- Una descripción concisa de una oración del propósito del crate
- Explicación detallada de los conceptos y características clave
- Ejemplos de inicio rápido que muestran el uso básico
- Descripción general de la arquitectura para bibliotecas más complejas
- Indicadores de características y opciones de configuración
- Información de compatibilidad
- Características de rendimiento
Documentación a nivel de módulo
Los módulos actúan como unidades organizativas en Rust y deben tener su propia documentación que explique su propósito y contenido:
pub mod symmetric {
//! Algoritmos de cifrado simétrico.
//!
//! Este módulo proporciona implementaciones de cifrados de bloque y de flujo,
//! algoritmos de cifrado autenticado y utilidades relacionadas.
//!
//! # Consideraciones de seguridad
//!
//! Todas las implementaciones han sido auditadas por [Empresa de seguridad]
//! y formalmente verificadas utilizando [Herramienta de verificación].
/// Implementación del cifrado de bloque AES con soporte para claves de 128, 192 y 256 bits.
pub struct Aes {
// Campos aquí
}
// Más elementos...
}
Documentación a nivel de elemento
Los elementos individuales como structs, funciones y traits deben tener una documentación enfocada que explique su propósito, uso y cualquier consideración especial:
/// Un generador de números aleatorios criptográficamente seguro.
///
/// Este CSPRNG utiliza la fuente de entropía del sistema para generar
/// bytes aleatorios adecuados para operaciones criptográficas.
///
/// # Ejemplos
///
/// ```
/// use crypto_lib::CSPRNG;
///
/// let mut rng = CSPRNG::new();
/// let random_bytes = rng.generate_bytes(32);
/// ```
///
/// # Seguridad
///
/// En Linux, esto usa getrandom(2) cuando está disponible, recurriendo a /dev/urandom.
/// En Windows, usa BCryptGenRandom.
/// En macOS, usa SecRandomCopyBytes.
pub struct CSPRNG {
// Detalles de implementación
}
impl CSPRNG {
/// Crea un nuevo generador de números aleatorios criptográficamente seguro.
///
/// # Pánicos
///
/// Entra en pánico si la fuente de entropía del sistema no está disponible.
///
/// # Ejemplos
///
/// ```
/// let rng = crypto_lib::CSPRNG::new();
/// ```
pub fn new() -> Self {
// Implementación
}
/// Genera el número especificado de bytes aleatorios.
///
/// # Argumentos
///
/// * `len` - El número de bytes aleatorios para generar
///
/// # Devoluciones
///
/// Un vector que contiene `len` bytes aleatorios criptográficamente seguros.
///
/// # Ejemplos
///
/// ```
/// use crypto_lib::CSPRNG;
///
/// let mut rng = CSPRNG::new();
/// let key_material = rng.generate_bytes(32);
/// assert_eq!(key_material.len(), 32);
/// ```
pub fn generate_bytes(&mut self, len: usize) -> Vec<u8> {
// Implementación
}
}
Pruebas de documentación en profundidad
Una de las características más poderosas de Rustdoc es su capacidad para ejecutar ejemplos de código como pruebas. Esto asegura que:
- Los ejemplos en su documentación realmente se compilan
- Los ejemplos producen los resultados esperados
- Los ejemplos se mantienen actualizados a medida que evoluciona su API
Cuando incluye un bloque de código Rust en su documentación, Rustdoc lo extrae y crea un arnés de prueba a su alrededor:
/// Suma dos números.
///
/// # Ejemplos
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Entre bastidores, Rustdoc transforma esto en un archivo de prueba independiente similar a:
extern crate my_crate;
fn main() {
let result = my_crate::add(2, 3);
assert_eq!(result, 5);
}
Este archivo luego se compila y se ejecuta. Si el programa se compila y se ejecuta sin entrar en pánico, la prueba pasa.
Preprocesamiento de pruebas
Antes de ejecutar la prueba, Rustdoc aplica varias transformaciones para hacer que los ejemplos simples sean más ergonómicos:
- Se permiten lints comunes como
unused_variables
ydead_code
- Si el ejemplo no contiene
extern crate
y no se especificó#![doc(test(no_crate_inject))]
, se insertaextern crate <mycrate>;
- Si el ejemplo no contiene
fn main
, el código se envuelve enfn main() { ... }
Estas transformaciones le permiten concentrarse en las partes importantes de su ejemplo sin código repetitivo.
Control del comportamiento de las pruebas
Rustdoc proporciona varios atributos para controlar cómo se ejecutan las pruebas:
ignore
: El código no se pruebashould_panic
: El código debe compilarse pero entrar en pánico cuando se ejecutacompile_fail
: El código no debe compilarseno_run
: El código se compila pero no se ejecutaedition2015
,edition2018
,edition2021
: Ejecute el código con una edición específica de Rust
Ejemplos:
/// ```ignore
/// // Este código no se prueba
/// let x = function_that_doesnt_exist();
/// ```
///
/// ```should_panic
/// // Este código debería entrar en pánico
/// panic!("Este ejemplo demuestra un pánico");
/// ```
///
/// ```compile_fail
/// // Este código no debería compilarse
/// let x: i32 = "esto no debería compilarse";
/// ```
///
/// ```no_run
/// // Este código se compila pero no se ejecuta
/// loop {
/// println!("¡Esto se ejecutaría para siempre!");
/// }
/// ```
///
/// ```edition2021
/// // Este código usa características de Rust 2021
/// let result = try {
/// "10".parse::<i32>()?
/// };
/// ```
Usando ?
en pruebas de documentación
Dado que los ejemplos de documentación están envueltos en una función main()
que devuelve ()
, el uso del operador ?
requiere un manejo especial. Hay dos enfoques:
- Defina explícitamente una función
main
que devuelva unResult
:
/// ```
/// # fn main() -> Result<(), std::io::Error> {
/// let content = std::fs::read_to_string("file.txt")?;
/// println!("Contenido del archivo: {}", content);
/// # Ok(())
/// # }
/// ```
2. Use una anotación de tipo con Ok(())
:
/// ```
/// let content = std::fs::read_to_string("file.txt")?;
/// println!("Contenido del archivo: {}", content);
/// # Ok::<(), std::io::Error>(())
/// ```
En ambos casos, el #
al principio de algunas líneas las oculta de la documentación renderizada, pero las incluye en la prueba.
Vinculación a elementos por nombre
Rustdoc proporciona un poderoso sistema de referencias cruzadas que permite a los autores de documentación crear enlaces a otros elementos en la base de código. Esta característica mejora significativamente la navegabilidad de su documentación.
Enlaces intra-documento
Para crear un enlace a otro elemento, use la sintaxis [
nombre_del_elemento]
:
/// Utiliza el tipo [`HashMap`] de la biblioteca estándar.
///
/// También se basa en la función [`build_map`] definida en otra parte de este crate.
pub fn process_data() {
// Implementación
}
/// Construye un nuevo [`HashMap`] con valores predefinidos.
pub fn build_map() -> HashMap<String, i32> {
// Implementación
}
Cuando Rustdoc procesa estos enlaces, los resuelve automáticamente a los elementos correctos, creando hipervínculos en los que se puede hacer clic en el HTML generado.
Calificación de ruta
Para una vinculación más precisa, especialmente cuando se trata de elementos que pueden tener el mismo nombre en diferentes módulos, puede usar rutas totalmente calificadas:
/// Esta función utiliza [`std::collections::HashMap`] para el almacenamiento
/// y [`crate::utils::format`] para formatear la salida.
pub fn complex_operation() {
// Implementación
}
La resolución de la ruta sigue las reglas de visibilidad de Rust, por lo que solo puede vincular a elementos que serían visibles desde el alcance actual.
Destinos de enlace
Puede vincular a varios tipos de elementos:
/// Enlaces a un struct: [`MyStruct`]
/// Enlaces a un enum: [`Option`]
/// Enlaces a un trait: [`Iterator`]
/// Enlaces a una función: [`process_data`]
/// Enlaces a un método: [`MyStruct::new`]
/// Enlaces a un módulo: [`crate::utils`]
/// Enlaces a una constante: [`MAX_SIZE`]
/// Enlaces a un alias de tipo: [`Result`]
Este sistema de vinculación integral permite a los autores de documentación crear una rica red de documentación interconectada que ayuda a los usuarios a navegar por API complejas.
Características avanzadas de la documentación
El atributo #[doc]
El atributo #[doc]
proporciona un control avanzado sobre la generación de documentación:
// Equivalente a usar comentarios ///
#[doc = "Esta es la documentación para el elemento a continuación."]
pub struct DocumentedStruct;
// Ocultar un elemento de la documentación
#[doc(hidden)]
pub struct InternalStruct;
// Agregar alias de búsqueda
#[doc(alias = "connection")]
#[doc(alias = "socket")]
pub struct NetworkStream;
Para documentación condicional basada en indicadores de características:
/// Esta función hace cosas increíbles.
#[doc = "Explicación de la capacidad básica."]
#[cfg_attr(feature = "advanced", doc = "También admite el modo avanzado cuando la característica 'advanced' está habilitada.")]
pub fn do_things() {
// Implementación
}
Patrones de inclusión de documentación
Para reutilizar contenido en la documentación:
/// # Seguridad
///
#[doc = include_str!("../docs/common_safety_notes.md")]
pub unsafe fn perform_unsafe_operation() {
// Implementación
}
Esto le permite mantener segmentos de documentación comunes en archivos separados e incluirlos donde sea necesario, reduciendo la duplicación y garantizando la coherencia.
Ejemplos extraídos
Rustdoc puede extraer automáticamente ejemplos de código de los directorios de pruebas y ejemplos de su crate, proporcionando contexto adicional para el uso de la API:
$ cargo doc --document-scraped-examples
Esta característica funciona escaneando su crate en busca de uso de elementos públicos y extrayendo esos usos como ejemplos. Es particularmente valioso para API complejas donde los ejemplos de documentación en línea pueden ser insuficientes.
Documentación condicional
Usando atributos cfg
, puede incluir o excluir documentación condicionalmente:
#[cfg(target_os = "windows")]
/// Detalles de implementación específicos de Windows.
pub fn windows_only_function() {
// Implementación de Windows
}
#[cfg(target_os = "linux")]
/// Detalles de implementación específicos de Linux.
pub fn linux_only_function() {
// Implementación de Linux
}
Esto le permite crear documentación específica de la plataforma o específica de la característica que solo aparece cuando es relevante.
HTML personalizado en la documentación
Para necesidades especiales de formato, Rustdoc permite incrustar HTML directamente en la documentación:
/// <div class="warning">
/// <strong>Advertencia:</strong> Esta función realiza E/S y puede bloquearse.
/// </div>
pub fn blocking_operation() {
// Implementación
}
Combinado con CSS personalizado, esto permite un formato enriquecido más allá de lo que proporciona Markdown por sí solo.
HTML y CSS personalizados
Rustdoc permite la personalización de la apariencia de la documentación generada a través de HTML y CSS:
Preprocesamiento de HTML
Puede agregar HTML personalizado al encabezado de la documentación:
$ rustdoc src/lib.rs --html-in-header custom-header.html
Esto es útil para agregar hojas de estilo personalizadas, bibliotecas de JavaScript o etiquetas meta.
Personalización de CSS
Para aplicar CSS personalizado a su documentación:
$ rustdoc src/lib.rs --css custom-styles.css
Su archivo CSS puede apuntar a la estructura HTML de Rustdoc para personalizar colores, fuentes, diseños y más:
/* Cambiar el color de fondo principal */
body {
background-color: #f5f5f5;
}
/* Estilizar bloques de advertencia */
.warning {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 0.5rem 1rem;
margin: 1rem 0;
}
/* Estilo personalizado para bloques de código */
pre {
background-color: #282c34;
border-radius: 6px;
padding: 1rem;
}
Temas de renderizado personalizados
Rustdoc admite múltiples temas integrados, incluidos Light, Rust, Coal, Navy y Ayu. También puede crear temas personalizados:
$ rustdoc src/lib.rs --theme mytheme.css
Mejores prácticas de documentación
Precisión técnica
La documentación debe ser técnicamente precisa. Especifique el comportamiento exacto, los casos extremos y las características de rendimiento:
/// Busca un elemento en un segmento ordenado usando la búsqueda binaria.
///
/// # Complejidad
///
/// Complejidad de tiempo: O(log n)
/// Complejidad de espacio: O(1)
///
/// # Pánicos
///
/// Entra en pánico si el segmento no está ordenado en orden ascendente.
///
/// # Ejemplos
///
/// ```
/// let sorted = [1, 2, 3, 4, 5];
/// assert_eq!(my_crate::binary_search(&sorted, 3), Some(2));
/// assert_eq!(my_crate::binary_search(&sorted, 6), None);
/// ```
pub fn binary_search<T: Ord>(slice: &[T], value: T) -> Option<usize> {
// Implementación
}
Documentación estructurada
Siga una estructura coherente para cada tipo de elemento:
Funciones y métodos:
- Descripción breve
- Explicaciones de los parámetros
- Descripción del valor de retorno
- Casos de error
- Sección de pánicos (si corresponde)
- Ejemplos
- Características de rendimiento
- Consideraciones de seguridad (para funciones
unsafe
)
Structs y enums:
- Propósito y descripción de alto nivel
- Explicaciones de campo/variante
- Métodos de construcción
- Operaciones comunes
- Ejemplos de uso típico
Traits:
- Contrato y garantías
- Explicación de los métodos requeridos
- Documentación de los métodos proporcionados
- Guía de implementación
- Ejemplos que muestran la implementación y el uso
Precisión del lenguaje
Use un lenguaje técnico preciso y evite la ambigüedad:
- En lugar de "rápido", especifique "complejidad de tiempo O(log n)"
- En lugar de "eficiente en memoria", especifique "usa espacio de pila constante"
- En lugar de "podría fallar", especifique las condiciones de error exactas
- En lugar de "entradas grandes", especifique limitaciones concretas
Información de control de versiones
Documente la estabilidad de la API y las consideraciones de control de versiones:
/// Procesa paquetes de red de acuerdo con RFC 1234.
///
/// # Estabilidad
///
/// Esta función se considera estable desde la versión 0.2.0.
///
/// # Diferencias de versión
///
/// Antes de la 0.3.0, esta función truncaría silenciosamente los paquetes
/// mayores de 1500 bytes. Ahora devuelve un error.
pub fn process_packet(packet: &[u8]) -> Result<ProcessedPacket, PacketError> {
// Implementación
}
Técnicas avanzadas de prueba
Pruebas en diferentes entornos
Puede configurar las pruebas de documentación para que se ejecuten con variables de entorno específicas:
/// ```
/// # std::env::set_var("API_KEY", "test_key");
/// let client = my_crate::Client::new_from_env()?;
/// # Ok::<(), my_crate::Error>(())
/// ```
Pruebas con recursos externos
Para las pruebas que necesitan recursos externos, use el atributo no_run
y explique los requisitos:
/// ```no_run
/// // Este ejemplo requiere una base de datos PostgreSQL en localhost:5432
/// // con nombre de usuario "test" y contraseña "test"
/// let db = my_crate::Database::connect(
/// "postgres://test:test@localhost:5432/testdb"
/// )?;
/// # Ok::<(), my_crate::Error>(())
/// ```
Prueba del manejo de errores
Muestre cómo se manejan los errores en su API:
/// ```
/// use my_crate::{process_data, DataError};
///
/// // Caso exitoso
/// let result = process_