¿Cuáles son las diferencias entre `String` y` str` de Rust?

421

¿Por qué tiene Rust Stringy str? ¿Cuáles son las diferencias entre Stringy str? ¿Cuándo se usa uno en Stringlugar de stry viceversa? ¿Uno de ellos está en desuso?

Daniel Fath
fuente

Respuestas:

491

Stringes el tipo de cadena dinámico dinámico, como Vec: utilícelo cuando necesite poseer o modificar sus datos de cadena.

stres una secuencia inmutable de 1 UTF-8 bytes de longitud dinámica en algún lugar de la memoria. Como el tamaño es desconocido, solo se puede manejar detrás de un puntero. Esto significa que strmás comúnmente 2 aparece como &str: una referencia a algunos datos UTF-8, normalmente llamados "corte de cadena" o simplemente un "corte". Un segmento es solo una vista de algunos datos, y esos datos pueden estar en cualquier lugar, por ejemplo

  • En almacenamiento estático : un literal de cadena "foo"es a &'static str. Los datos se codifican en el ejecutable y se cargan en la memoria cuando se ejecuta el programa.
  • Dentro de un montón asignadoString : Stringdesreferencias a una &strvista de los Stringdatos del 's.
  • En la pila : por ejemplo, lo siguiente crea una matriz de bytes asignada a la pila y luego obtiene una vista de esos datos como&str :

    use std::str;
    
    let x: &[u8] = &[b'a', b'b', b'c'];
    let stack_str: &str = str::from_utf8(x).unwrap();

En resumen, úselo Stringsi necesita datos de cadena propios (como pasar cadenas a otros hilos o construirlos en tiempo de ejecución), y úselo &strsi solo necesita una vista de una cadena.

Esto es idéntico a la relación entre un vector Vec<T>y un corte &[T], y es similar a la relación entre el valor Ty la referencia &Tpara los tipos generales.


1 A stres de longitud fija; no puede escribir bytes más allá del final, ni dejar bytes finales inválidos. Como UTF-8 es una codificación de ancho variable, esto obliga a todos los strs a ser inmutables en muchos casos. En general, la mutación requiere escribir más o menos bytes que antes (por ejemplo, reemplazar un a(1 byte) con un ä(2+ bytes) requeriría hacer más espacio en el str). Existen métodos específicos que pueden modificar un &strlugar, principalmente aquellos que manejan solo caracteres ASCII, como make_ascii_uppercase.

2 Los tipos de tamaño dinámico permiten cosas como Rc<str>una secuencia de bytes UTF-8 contados de referencia desde Rust 1.2. Rust 1.21 permite crear fácilmente estos tipos.

huon
fuente
10
"secuencia de bytes UTF-8 ( de longitud desconocida )" - ¿está desactualizado? Los documentos dicen que "A &strestá formado por dos componentes: un puntero a algunos bytes y una longitud".
mrec
11
No es fuera de fecha (que la representación ha sido bastante estable), sólo un poco imprecisa: no se sabe de forma estática, a diferencia de, por ejemplo, [u8; N].
Huon
2
@mrec se desconoce en el momento de la compilación, no se pueden hacer suposiciones sobre su tamaño, por ejemplo, al crear un marco de pila. Por lo tanto, por qué a menudo se trata como una referencia, que es un tamaño conocido en tiempo de compilación, que es el tamaño de un puntero.
Sekhat
1
Actualización: Rc<str>y Arc<str>ahora se pueden usar a través de la biblioteca estándar.
Centril
1
@cjohansson Los objetos asignados estáticamente normalmente no se almacenan ni en el montón ni en la pila, sino en su propia región de memoria.
Brennan Vincent
97

Tengo experiencia en C ++ y me pareció muy útil pensar Stringy &stren términos de C ++:

  • Un óxido Stringes como un std::string; posee la memoria y hace el trabajo sucio de administrar la memoria.
  • Un óxido &stres como un char*(pero un poco más sofisticado); nos señala el comienzo de un fragmento de la misma manera en que puede obtener un puntero al contenido de std::string.

¿Alguno de ellos va a desaparecer? No lo creo. Tienen dos propósitos:

Stringmantiene el búfer y es muy práctico de usar. &stres liviano y debe usarse para "mirar" en cuerdas. Puede buscar, dividir, analizar e incluso reemplazar fragmentos sin necesidad de asignar nueva memoria.

&strpuede mirar dentro de una, Stringya que puede apuntar a una cadena literal. El siguiente código debe copiar la cadena literal en la Stringmemoria administrada:

let a: String = "hello rust".into();

El siguiente código le permite usar el literal en sí mismo sin copia (aunque solo lectura)

let a: &str = "hello rust";
Luis Ayuso
fuente
13
como un string_view?
Abhinav Gauniyal el
2
Sí, como string_view pero intrínseco al lenguaje y debidamente prestado marcado.
locka
41

str, solo se usa como &str, es un segmento de cadena, una referencia a una matriz de bytes UTF-8.

Stringes lo que solía ser ~str, una matriz de bytes UTF-8 que se puede crecer y poseer.

Chris Morgan
fuente
Técnicamente, lo que solía ser ~strahora esBox<str>
jv110
3
@ jv110: no, porque ~strera cultivable mientras Box<str>que no es cultivable. (Eso ~stry ~[T]fueron mágicamente cultivables, a diferencia de cualquier otro objeto ~, fue exactamente por qué Stringy Vec<T>se introdujeron, de modo que las reglas fueron directas y consistentes.)
Chris Morgan
18

En realidad son completamente diferentes. En primer lugar, a strno es más que una cosa de nivel de tipo; solo se puede razonar a nivel de tipo porque es un tipo denominado de tamaño dinámico (DST). El tamaño que strocupa no se puede conocer en tiempo de compilación y depende de la información de tiempo de ejecución; no se puede almacenar en una variable porque el compilador necesita saber en tiempo de compilación cuál es el tamaño de cada variable. A stres conceptualmente solo una fila de u8bytes con la garantía de que forma un UTF-8 válido. ¿Qué tan grande es la fila? Nadie lo sabe hasta el tiempo de ejecución, por lo tanto, no se puede almacenar en una variable.

Lo interesante es que una &stro cualquier otro puntero a una strcomo Box<str> lo hace existir en tiempo de ejecución. Este es un llamado "indicador gordo"; es un puntero con información adicional (en este caso, el tamaño de la cosa a la que apunta), por lo que es el doble de grande. De hecho, a &strestá bastante cerca de a String(pero no a &String). A &strson dos palabras; un puntero al primer byte de ay strotro número que describe cuántos bytes tiene la longitud del str.

Contrariamente a lo que se dice, a strno necesita ser inmutable. Si puede obtener un &mut strcomo puntero exclusivo para el str, puede mutarlo y todas las funciones seguras que lo mutan garantizan que la restricción UTF-8 se mantenga porque si se viola, entonces tenemos un comportamiento indefinido ya que la biblioteca asume que esta restricción es cierto y no lo comprueba.

Entonces, ¿qué es un String? Eso son tres palabras; dos son los mismos que para &strpero agrega una tercera palabra que es la capacidad del strbúfer en el montón, siempre en el montón (a strno está necesariamente en el montón) que administra antes de que se llene y tenga que reasignarse. el Stringbásicamente posee una strcomo dicen; lo controla y puede cambiar su tamaño y reasignarlo cuando lo considere conveniente. Entonces a Stringes como se dice más cerca de a &strque a a str.

Otra cosa es una Box<str>; esto también posee a stry su representación en tiempo de ejecución es la misma que a &strpero también posee la strdiferencia, &strpero no puede cambiar su tamaño porque no conoce su capacidad, por lo que básicamente Box<str>se puede ver como una longitud fija Stringque no se puede cambiar de tamaño (puede siempre conviértalo en un Stringsi desea cambiar su tamaño).

Existe una relación muy similar entre [T]y Vec<T>excepto que no hay restricción UTF-8 y puede contener cualquier tipo cuyo tamaño no sea dinámico.

El uso de stren el nivel de tipo es principalmente para crear abstracciones genéricas con &str; existe en el nivel de tipo para poder escribir rasgos convenientemente. En teoría, strcomo una cosa tipo no necesitaba existir y solo &streso significaría que se tendría que escribir una gran cantidad de código adicional que ahora puede ser genérico.

&stres súper útil para poder tener múltiples subcadenas diferentes de a Stringsin tener que copiar; como se dijo, String posee el stren el montón que administra y si solo pudiera crear una subcadena de a Stringcon un nuevo String, tendría que copiarlo porque todo en Rust solo puede tener un único propietario para lidiar con la seguridad de la memoria. Entonces, por ejemplo, puede cortar una cadena:

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

Tenemos dos subcadenas diferentes strde la misma cadena. stringes el propietario del strbúfer completo real en el montón y las &strsubcadenas son solo punteros gordos a ese búfer en el montón.

Zorf
fuente
4

std::Stringes simplemente un vector de u8. Puede encontrar su definición en el código fuente . Está asignado al montón y puede crecer.

#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
    vec: Vec<u8>,
}

stres un tipo primitivo, también llamado corte de cadena . Un corte de cadena tiene un tamaño fijo. Una cadena literal como let test = "hello world"tiene &'static strtipo. testes una referencia a esta cadena asignada estáticamente. &strno se puede modificar, por ejemplo,

let mut word = "hello world";
word[0] = 's';
word.push('\n');

strtiene una porción mutable &mut str, por ejemplo: pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)

let mut s = "Per Martin-Löf".to_string();
{
    let (first, last) = s.split_at_mut(3);
    first.make_ascii_uppercase();
    assert_eq!("PER", first);
    assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);

Pero un pequeño cambio en UTF-8 puede cambiar la longitud de su byte, y un segmento no puede reasignar su referente.

Aperion
fuente
0

En palabras sencillas, el Stringtipo de datos se almacena en el montón (al igual que Vec) y tiene acceso a esa ubicación.

&strEs un tipo de corte. Eso significa que es solo referencia a un ya presente Stringen algún lugar del montón.

&strno hace ninguna asignación en tiempo de ejecución. Entonces, por razones de memoria, puede usar &strmás String. Pero tenga en cuenta que al usarlo &strpuede tener que lidiar con vidas explícitas.

00imvj00
fuente
1
en algún lugar del montón , eso no es completamente exacto.
Shepmaster
Lo que quise decir stres que viewya está presente Stringen el montón.
00imvj00
1
Entiendo que eso es lo que quisiste decir, y digo que eso no es completamente exacto. El "montón" no es una parte obligatoria de la declaración.
Shepmaster
-1

Para personas de C # y Java:

  • Óxido ' String===StringBuilder
  • &str Cadena de óxido === (inmutable)

Me gusta pensar en a &strcomo una vista en una cadena, como una cadena interna en Java / C # donde no puede cambiarla, solo cree una nueva.

Ardilla
fuente
1
La mayor diferencia entre las cadenas Java / C # y las cadenas Rust es que Rust garantiza que la cadena sea unicode correcta, por lo que obtener el tercer carácter en una cadena requiere más reflexión que simplemente "abc" [2]. (Dado que vivimos en un mundo multilingüe, esto es algo bueno.)
Squirrel
Esto es incorrecto . El tema de la mutabilidad ya se aborda en la respuesta más votada; por favor léalo para aprender más.
Shepmaster
-5

Aquí hay una explicación rápida y fácil.

String- Una estructura de datos atribuible a almacenamiento dinámico que se puede crecer. Se puede obligar a a &str.

str- es (ahora, a medida que Rust evoluciona) una cadena mutable de longitud fija que vive en el montón o en el binario. Solo puede interactuar con strun tipo prestado a través de una vista de corte de cadena, como &str.

Consideraciones de uso:

Prefiera Stringsi desea poseer o mutar una cadena, como pasar la cadena a otro hilo, etc.

Prefiere &strsi quieres tener una vista de solo lectura de una cadena.

Desarrollador
fuente
Esto es incorrecto . El tema de la mutabilidad ya se aborda en la respuesta más votada; por favor léalo para aprender más.
Shepmaster