Si trabajas con datos chilenos vas a tener que validar RUTs. La gente los tipea con puntos, sin puntos, con guión, sin guión, con la K en mayúscula o minúscula. Y siempre, siempre, alguien va a meter un RUT inválido en el formulario.

Acá está el algoritmo oficial del dígito verificador en Go, sin dependencias, en menos de 30 líneas. Te lo puedes copiar tal cual.

El algoritmo

El RUT chileno tiene un dígito verificador (DV) calculado así:

  1. Toma el cuerpo del RUT (los dígitos antes del DV).
  2. Multiplica cada dígito, de derecha a izquierda, por la serie 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7… (cíclica).
  3. Suma todos esos productos.
  4. Calcula 11 - (suma mod 11).
  5. Si el resultado es 11, el DV es 0. Si es 10, el DV es la letra K. En cualquier otro caso, el DV es ese mismo número.

El código

package rut

import (
	"strconv"
	"strings"
)

// Valid retorna true si el RUT es válido. Acepta formatos como
// "12345678-9", "12.345.678-9", "12345678-K", "12345678K".
func Valid(rut string) bool {
	rut = strings.ToUpper(strings.NewReplacer(".", "", "-", "", " ", "").Replace(rut))
	if len(rut) < 2 {
		return false
	}
	body, dv := rut[:len(rut)-1], rut[len(rut)-1]
	n, err := strconv.Atoi(body)
	if err != nil || n < 1 {
		return false
	}
	mult := 2
	sum := 0
	for n > 0 {
		sum += (n % 10) * mult
		n /= 10
		mult++
		if mult > 7 {
			mult = 2
		}
	}
	expected := byte('0' + (11-sum%11)%11)
	if 11-sum%11 == 10 {
		expected = 'K'
	}
	return dv == expected
}

Eso es todo. Dos imports de la stdlib, cero dependencias externas.

Tests rápidos

package rut

import "testing"

func TestValid(t *testing.T) {
	cases := []struct {
		input string
		want  bool
	}{
		{"11.111.111-1", true},
		{"11111111-1", true},
		{"22.222.222-2", true},
		{"15.873.097-K", true},
		{"15873097K", true},
		{"15873097-k", true},  // case-insensitive
		{"12345678-9", false}, // dv incorrecto
		{"abc", false},
		{"", false},
	}
	for _, c := range cases {
		if got := Valid(c.input); got != c.want {
			t.Errorf("Valid(%q) = %v, want %v", c.input, got, c.want)
		}
	}
}

Cómo lo usamos en formularios HTMX

Cuando validas RUTs en un formulario HTMX renderizado en el servidor, el flujo queda así:

http.HandleFunc("POST /clientes", func(w http.ResponseWriter, r *http.Request) {
	rutInput := r.FormValue("rut")
	if !rut.Valid(rutInput) {
		w.WriteHeader(http.StatusUnprocessableEntity)
		w.Write([]byte(`<span class="error">RUT inválido</span>`))
		return
	}
	// ...continuar con el guardado
})

Sin librerías de validación, sin schemas, sin Zod ni Yup. El servidor valida, devuelve HTML, HTMX lo inserta donde corresponda.

Una nota sobre formato

Hay dos cuestiones distintas que la gente confunde:

  1. Validar el DV: lo que hace este código. Comprueba que el RUT matemáticamente bien formado.
  2. Verificar que el RUT exista: requiere consultar al SII (Servicio de Impuestos Internos), un servicio externo. Es otra capa.

Para 99% de los formularios de PYMES, validar el DV es suficiente. Si necesitas verificar contra el SII (por ejemplo para emisión de documentos tributarios), eso es un proyecto distinto.


¿Tienes un sistema chileno que necesita validar datos locales y quieres dejar de pelear con strings? Cuéntanos por WhatsApp o correo.

← Volver a notas