string - ¿Cómo represento una cadena opcional en Go?
null optional (2)
Deseo modelar un valor que puede tener dos formas posibles: ausente o una cadena.
La forma natural de hacerlo es con Maybe String
u Optional<String>
, o la string option
, etc. Sin embargo, Go no tiene tipos de variantes como este.
Entonces pensé, siguiendo a Java, C, etc., que la alternativa sería la capacidad de anulación, o nil
en Go. Sin embargo, nil
no es un miembro del tipo de string
en Go.
Al buscar, pensé usar el tipo *string
. Esto podría funcionar, pero parece muy incómodo (por ejemplo, no puedo tomar la dirección del literal de la cadena de la misma manera que puedo tomar la dirección de una estructura literal).
¿Cuál es la forma idiomática de modelar tal valor en Go?
Podrías usar algo como sql.NullString
, pero personalmente me quedaría con *string
. En cuanto a la incomodidad, es cierto que no se puede simplemente sp := &"foo"
desgracia. Pero hay una solución para esto:
func strPtr(s string) *string {
return &s
}
Las llamadas a strPtr("foo")
deben estar en línea, por lo que es eficaz &"foo"
.
Otra posibilidad es usar new
:
sp := new(string)
*sp = "foo"
Una solución lógica sería usar *string
como lo menciona Ainar-G. Esta otra respuesta detalla las posibilidades de obtener un puntero a un valor ( int64
pero lo mismo también funciona con la string
). Un contenedor es otra solución.
Usando solo string
Una string
opcional significa una string
más 1 valor específico (o estado) que dice "no una cadena" (sino un null
).
Este 1 valor específico se puede almacenar (señalizar) en otra variable (por ejemplo, bool
) y puedes empaquetar la string
y el bool
en una struct
y llegamos al contenedor, pero esto no encaja en el caso de "usar solo una string
"(pero sigue siendo una solución viable).
Si queremos apegarnos solo a una string
, podemos sacar 1 valor específico de los posibles valores de un tipo de string
(que tiene valores posibles de "infinito" ya que la longitud no está limitada (o tal vez es como debe ser una int
pero eso está bien)), y podemos nombrar este valor específico como el valor null
, el valor que significa "no una cadena".
El valor más conveniente para indicar null
es el valor cero de la string
, que es la string
vacía: ""
. Al designar esto, el elemento null
tiene la conveniencia de que cada vez que cree una variable de string
sin especificar explícitamente el valor inicial, se inicializará con ""
. Además, al consultar un elemento de un map
cuyo valor es una string
también arrojará ""
si la clave no está en el map
.
Esta solución se adapta a muchos casos de uso de la vida real. Si se supone que la string
opcional es el nombre de una persona, por ejemplo, una string
vacía realmente no significa un nombre de persona válido, por lo que no debe permitir eso en primer lugar.
Puede haber casos, por supuesto, cuando la string
vacía representa un valor válido de una variable del tipo de string
. Para estos casos de uso, podemos elegir otro valor.
En Go, una string
es en efecto una porción de bytes de solo lectura. Consulte la publicación del blog Cadenas, bytes, runas y caracteres en Ir que explica esto en detalle.
Por lo tanto, una string
es un segmento de bytes, que es el byte codificado UTF-8 en el caso de un texto válido. Suponiendo que desea almacenar un texto válido en su string
opcional (si no lo hace, entonces puede usar un []byte
lugar que puede tener un valor nil
), puede elegir un valor de string
que represente un UTF-8 no válido secuencia de bytes y por lo tanto ni siquiera tendrá que hacer un compromiso para excluir un texto válido de los posibles valores. La secuencia de bytes UTF-8 inválida más corta es de 1 byte solamente, por ejemplo 0xff
(hay más). Nota: puede usar la función utf8.ValidString()
para indicar si un valor de string
es un texto válido (secuencia de bytes codificada en UTF-8 válida).
Puedes hacer de este valor excepcional una const
:
const Null = "/xff"
Ser tan corto también significa que será muy rápido comprobar si una string
es igual a esto.
Y según esta convención, usted ya tiene una string
opcional que también permite la string
vacía.
Pruébalo en el patio de juegos Go .
const Null = "/xff"
func main() {
fmt.Println(utf8.ValidString(Null)) // false
s := Null
fmt.Println([]byte(s)) // [255]
fmt.Println(s == Null) // true
s = "notnull"
fmt.Println(s == Null) // false
}