вторник, 8 февраля 2022 г.

Длина строк в Go

Этот пост родился из-за странного поведения валидатора Length из пакета ozzo-validation.

Следуя руководству, можно зарегистрировать, например, такой валидатор для строк:

data := "Некоторая строка"
err := validation.Validate(data,
    validation.Required,
    validation.Length(10, 16),
)
fmt.Println(err)

И так как в строке у нас кириллические символы, и более того - кодировка строки utf-8, то валидация не пропускает строку, падая с ошибкой "the length must be between 10 and 16". Почему так происходит, ведь валидируемая строка содержит ровно 16 символов?

Для того, чтобы понять почему валидация выдает ошибку - придется сходить в исходный код валидатора. Поиски приводят к тому, что для определения длины валидируемой строки используется метод Len() из типа reflect#Value. Путешествие дальше приводит нас к тому, что свойство Len вычисляется с помощью встроенной функции len. А как сообщает мануал:

The len built-in function returns the length of v, according to its type:
String: the number of bytes in v.

Следовательно, валидация на основании длины строки в байтах нас не устраивает, так как в случае многобайтных кодировок (коей является utf-8) число символов в строке не равно числу байт в этой же строке.

Хотелось бы, чтобы был метод типа utfLen(). И он есть в пакете unicode/utf8, правда, с немного другим названием - RuneCountInString. И о чудо, данный метод даже используется в валидаторе, если установить дополнительный флаг rune. И это можно сделать применив валидатор RuneLength.

Обновленный код:

data := "Некоторая строка"
err := validation.Validate(data,
    validation.Required,
    validation.RuneLength(10, 16),
)
fmt.Println(err)

Валидация отрабатывает как и ожидается, выводя <nil>.

И не забудьте написать для этого случая тест (ну или хотя бы комментарий), чтобы пытливый программист, использующий код после вас, не смог бы заменить RuneLength на Length, думая, что "и так сойдёт".

P.S. Больше разъяснений про длины строк можно почитать в этом ответе на stackoverflow.

P.P.S. Отличное видео на Youtube, раскрывающее суть кодировок.