Qué es parse_number() y por qué tus datos lo necesitan
Convertir una cadena como -$1,234.56 o 12.5% en un valor numérico que SQLite pueda sumar y ordenar cuesta 16 nanosegundos por fila en la mejor implementación en C, frente a 41 nanosegundos en la versión más rápida escrita en Rust. Esa es la conclusión central del experimento que Simon Willison publicó el 8 de noviembre en su blog, con el código y los tests alojados en su repositorio simonw/research.
El problema que resuelve es cotidiano para cualquier equipo que hace analítica: los datos llegan como texto. Un CSV exportado de un CRM trae "$1,234.56", un reporte de marketing trae "12.5%", una planilla financiera trae "-$980". SQLite no puede sumar eso directamente, y la alternativa habitual —encadenar replace() para limpiar comas, signos de peso y porcentajes— es lenta y frágil.
La función parse_number(value) hace exactamente eso: quita ,, $, £ y %, respeta un signo negativo inicial y devuelve NULL cuando no puede interpretar el texto. Simple en la definición, interesante en la implementación.
¿Y esto cómo se aplica en tu negocio?
Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.
👥 Quiero el Kit →C vs Rust: los números del experimento
Willison partió de un prompt a Claude (Opus 5.5) pidiendo comparar la misma extensión escrita en C y en Rust, midiendo rendimiento, complejidad del código y tamaño del binario resultante. El primer resultado fue contraintuitivo: la versión en Rust era más rápida que la de C, porque el equivalente en Rust de strtod —la función estándar de C para convertir texto a número— resultaba aproximadamente el doble de veloz.
El segundo paso fue pedirle a Claude una función propia de parseo en C que alcanzara o superara a Rust. Lo logró. Estos son los costos netos por fila sobre 2 millones de filas que reporta el informe (menor es mejor):
| Escenario | C original | C optimizado | rusqlite | sqlite-loadable |
|---|---|---|---|---|
| Datos mixtos | 67 ns | 19 ns | 46 ns | 41 ns |
| Solo enteros | 25 ns | 16 ns | 36 ns | 32 ns |
| Solo decimales | 106 ns | 18 ns | 55 ns | 47 ns |
El C optimizado quedó entre 2 y 2,5 veces más rápido que las dos implementaciones en Rust, y muy por delante de la solución basada en replace() de SQL. Además, las cuatro versiones coincidieron en 8 millones de verificaciones de tipo fuzz, lo que da una señal razonable de que la optimización no rompió la semántica.
Por qué el C «lento» se volvió el más rápido
La diferencia está en un detalle de implementación, no en el lenguaje. La primera versión en C delegaba el parseo de decimales en strtod(), una función genérica que arrastra validaciones y manejo de casos que aquí no hacen falta. Eso la hacía especialmente lenta con decimales: 106 ns por fila.
La versión optimizada agregó un camino rápido y compacto para los formatos que realmente aparecen en datos de negocio, y el costo cayó a 18 ns en ese mismo escenario. La lección es transferible: en procesamiento de datos, el cuello de botella casi nunca es el lenguaje, sino cuánto trabajo genérico estás haciendo por cada registro.
El tamaño del binario: 14 KB contra 321 KB
El otro eje de la comparación es el peso del artefacto compilado. Según el informe, la extensión en C ocupa 14 KB, mientras que las versiones en Rust llegan a 321 KB con Rusqlite y 298 KB con sqlite-loadable-rs.
Esa brecha es consistente con lo que documenta el propio proyecto sqlite-loadable-rs de Alex Garcia: su README señala que una extensión «hello world» en C pesa 17 KB y una equivalente en Rust 469 KB, mientras que una en Go ronda los 2,2 MB. El mismo README aclara que, en general, una extensión construida con sqlite-loadable-rs es entre 10% y 15% más lenta que una en C, aunque también advierte que eso depende del trabajo real de cada extensión: cita que sqlite-xsv es 1,5 a 1,7 veces más rápido que la extensión CSV oficial escrita en C, y que sqlite-regex duplica la velocidad de la extensión regexp.
Es decir: Rust no es intrínsecamente más lento. El framework y el tipo de operación pesan más que el lenguaje.
¿Qué significa esto para tu startup?
Si tu equipo procesa datos en SQLite —y con la cantidad de productos que hoy embeben SQLite en el cliente, es probable que sí— la decisión entre C y Rust no debería tomarla el gusto del equipo, sino el perfil del problema.
- Mide antes de elegir lenguaje. El caso de
parse_number()muestra que una implementación en C puede ser 2x más rápida o 2x más lenta que Rust según cómo esté escrita. Un benchmark de 2 millones de filas con tus datos reales te cuesta una tarde y evita meses de arrepentimiento. - Prioriza el camino rápido para tus formatos reales. Si el 95% de tus valores vienen en tres o cuatro formatos, escribir un parser específico para esos casos rinde mucho más que optimizar el caso genérico. Fue exactamente lo que cerró la brecha de decimales en C.
- Considera el peso del binario si distribuyes en el cliente. 14 KB contra 321 KB no importa en un servidor, pero sí importa si tu extensión viaja dentro de una app de escritorio, un plugin o un entorno embebido con restricciones de tamaño.
- No descartes Rust por el tamaño. El ecosistema ya resolvió buena parte del problema: desde rusqlite 0.30.0 existe soporte de extensiones cargables con la feature
loadable_extension, y proyectos como sqlite-regex demuestran que Rust puede superar a C en tareas específicas.
Cómo aprovechar el informe en tu próximo sprint
El valor práctico del reporte de Willison no está en el resultado —C gana— sino en el método. Se formuló una pregunta concreta, se generaron cuatro implementaciones comparables, se midieron con el mismo dataset y se validaron con fuzz testing antes de sacar conclusiones.
Ese flujo es replicable con cualquier herramienta de IA generativa: pedir dos o tres variantes de la misma solución, exigir una tabla de rendimiento y un conjunto de tests que verifiquen equivalencia funcional. La parte que sigue siendo tuya es definir qué se mide y con qué datos.
Para un founder técnico, la conclusión más útil es que el costo de escribir una extensión propia de SQLite bajó lo suficiente como para dejar de depender de replace() en consultas críticas. Y que la respuesta a «¿C o Rust?» es, otra vez, «depende de qué estés midiendo».
Fuentes
- parse_number() as a SQLite extension: C vs Rust (fuente original)
- simonw/research: sqlite-parse-number-c-vs-rust
- asg017/sqlite-loadable-rs
¿Y esto cómo se aplica en tu negocio?
Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.
👥 Quiero el Kit →













