Por que Rust tem String
e str
? Quais são as diferenças entre String
e str
? Quando alguém usa em String
vez de str
e vice-versa? Um deles está sendo preterido?
String
é o tipo de cadeia de heap dinâmico, como Vec
: use-o quando precisar possuir ou modificar seus dados de cadeia.
str
é uma sequência imutável 1 de bytes UTF-8 de comprimento dinâmico em algum lugar da memória. Como o tamanho é desconhecido, só é possível manipulá-lo atrás de um ponteiro. Isso significa que, str
geralmente, 2 aparece como &str
: uma referência a alguns dados UTF-8, normalmente chamados de "fatia de string" ou apenas "fatia". Uma fatia é apenas uma visualização de alguns dados e esses dados podem estar em qualquer lugar, por exemplo
"foo"
é a &'static str
. Os dados são codificados no executável e carregados na memória quando o programa é executado.String
: String
desreferências para uma &str
visualização dos String
dados.Na pilha : por exemplo, o seguinte cria uma matriz de bytes alocados à pilha e obtém uma visualização desses dados como&str
:
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
Em resumo, use String
se você precisar de dados de sequência de propriedade (como passar sequências para outros encadeamentos ou construí-los em tempo de execução) e use &str
se precisar apenas de uma exibição de uma sequência.
É idêntico ao relacionamento entre um vetor Vec<T>
e uma fatia &[T]
e é semelhante ao relacionamento entre valor T
e referência &T
para tipos gerais.
1 A str
é de comprimento fixo; você não pode escrever bytes além do final nem deixar bytes inválidos à direita. Como o UTF-8 é uma codificação de largura variável, isso efetivamente força todos os str
s a serem imutáveis em muitos casos. Em geral, a mutação requer a gravação de mais ou menos bytes do que havia antes (por exemplo, substituir um a
(1 byte) por um ä
(2 + bytes) exigiria mais espaço no str
). Existem métodos específicos que podem modificar um &str
local, principalmente aqueles que manipulam apenas caracteres ASCII, como make_ascii_uppercase
.
2 Tipos de tamanho dinâmico permitem coisas como Rc<str>
uma sequência de referência contada em UTF-8 bytes desde o Rust 1.2. O Rust 1.21 permite criar facilmente esses tipos.
&str
é composto de dois componentes: um ponteiro para alguns bytes e um comprimento".[u8; N]
,.Rc<str>
eArc<str>
agora são utilizáveis através da biblioteca padrão.Tenho experiência em C ++ e achei muito útil pensar
String
e&str
em termos de C ++:String
é como umastd::string
; possui a memória e faz o trabalho sujo de gerenciar memória.&str
é como umchar*
(mas um pouco mais sofisticado); ele nos indica o início de um pedaço da mesma maneira que você pode obter um ponteiro para o conteúdo destd::string
.Algum deles vai desaparecer? Eu penso que não. Eles servem a dois propósitos:
String
mantém o buffer e é muito prático de usar.&str
é leve e deve ser usado para "olhar" em strings. Você pode pesquisar, dividir, analisar e até substituir pedaços sem precisar alocar nova memória.&str
pode olhar dentro de umString
, pois pode apontar para alguma string literal. O código a seguir precisa copiar a seqüência literal naString
memória gerenciada:O código a seguir permite que você use o literal em si sem copiar (somente leitura)
fonte
str
, usado apenas como&str
, é uma fatia de sequência, uma referência a uma matriz de bytes UTF-8.String
é o que costumava ser~str
, uma matriz UTF-8 de bytes cultivável e de propriedade.fonte
~str
é agoraBox<str>
~str
era cultivável enquantoBox<str>
não é cultivável. (Que~str
e~[T]
foram magicamente growable, diferente de qualquer outro~
-object, foi exatamente por issoString
eVec<T>
foram introduzidos, de modo que as regras eram todas simples e consistente.)Eles são realmente completamente diferentes. Primeiro, a
str
nada mais é que uma coisa de nível de tipo; ele só pode ser fundamentado no nível de tipo porque é o chamado tipo de tamanho dinâmico (DST). O tamanho questr
ocupa não pode ser conhecido no tempo de compilação e depende das informações de tempo de execução - não pode ser armazenado em uma variável porque o compilador precisa saber em tempo de compilação qual é o tamanho de cada variável. Astr
é conceitualmente apenas uma linha deu8
bytes com a garantia de que forma UTF-8 válido. Qual é o tamanho da linha? Ninguém sabe até o tempo de execução, portanto, ele não pode ser armazenado em uma variável.O interessante é que uma
&str
ou qualquer outro ponteiro para umstr
comoBox<str>
faz existir em tempo de execução. Este é o chamado "ponteiro gordo"; é um ponteiro com informações extras (nesse caso, o tamanho da coisa para a qual está apontando) e, portanto, é duas vezes maior. De fato, a&str
é bem próximo aString
(mas não a&String
). A&str
são duas palavras; um ponteiro para o primeiro byte de umstr
e outro número que descreve quantos bytes o comprimentostr
é.Ao contrário do que é dito, a
str
não precisa ser imutável. Se você pode obter um&mut str
ponteiro exclusivo para thestr
, é possível modificá-lo e todas as funções seguras que o modificam garantem que a restrição UTF-8 seja mantida, porque se isso for violado, teremos um comportamento indefinido, pois a biblioteca assume que essa restrição é true e não verifica isso.Então, o que é um
String
? São três palavras; duas são iguais a para,&str
mas adiciona uma terceira palavra que é a capacidade dostr
buffer no heap, sempre no heap (astr
não está necessariamente no heap) que ele gerencia antes de ser preenchido e precisa ser realocado. oString
basicamente possui umstr
como eles dizem; ele controla e pode redimensioná-lo e realocá-lo quando achar necessário. Portanto, aString
é como dito mais próximo de a do&str
que astr
.Outra coisa é uma
Box<str>
; isso também possui astr
e sua representação em tempo de execução é a mesma que a,&str
mas também possui astr
diferença de&str
mas não pode redimensioná-la porque não conhece sua capacidade; portanto, basicamente aBox<str>
pode ser vista como um comprimento fixoString
que não pode ser redimensionado (você pode sempre o converta em umString
se você quiser redimensioná-lo).Existe um relacionamento muito semelhante entre
[T]
eVec<T>
exceto que não há restrição UTF-8 e pode conter qualquer tipo cujo tamanho não seja dinâmico.O uso de
str
no nível de tipo é principalmente para criar abstrações genéricas com&str
; existe no nível de tipo para poder escrever convenientemente traços. Em teoria,str
como um tipo de coisa não precisava existir e apenas&str
isso significava que seria necessário escrever muito código extra que agora pode ser genérico.&str
é super útil para poder ter várias substrings diferentes de aString
sem ter que copiar; como foi dito, oString
proprietário é ostr
que gerencia e, se você pudesse criar uma subcadeia de umString
com um novoString
, teria que copiar, porque tudo no Rust pode ter apenas um único proprietário para lidar com a segurança da memória. Por exemplo, você pode cortar uma string:Temos duas substring
str
s diferentes da mesma string.string
é aquele que possui ostr
buffer completo real no heap e as&str
substrings são apenas indicadores de gordura para esse buffer no heap.fonte
std::String
é simplesmente um vetor deu8
. Você pode encontrar sua definição no código fonte . É alocado para a pilha e cultivável.str
é um tipo primitivo, também chamado de fatia de string . Uma fatia de sequência tem tamanho fixo. Uma string literal comolet test = "hello world"
has&'static str
type.test
é uma referência a essa sequência alocada estaticamente.&str
não pode ser modificado, por exemplo,str
tem fatia mutável&mut str
, por exemplo:pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
Mas uma pequena alteração no UTF-8 pode alterar o tamanho do byte e uma fatia não pode realocar seu referente.
fonte
Em palavras fáceis, o
String
tipo de dados é armazenado no heap (assim comoVec
) e você tem acesso a esse local.&str
é um tipo de fatia. Isso significa que é apenas uma referência a um já presenteString
em algum lugar da pilha.&str
não faz nenhuma alocação em tempo de execução. Portanto, por motivos de memória, você pode usar&str
maisString
. Mas lembre-se de que, ao usar,&str
você pode ter que lidar com vidas explícitas.fonte
str
éview
de já presenteString
no heap.Para pessoas em C # e Java:
String
===StringBuilder
&str
=== (imutável) stringEu gosto de pensar em
&str
como uma exibição em uma string, como uma string interna em Java / C # onde você não pode alterá-la, apenas crie uma nova.fonte
Aqui está uma explicação rápida e fácil.
String
- Uma estrutura de dados alocável em pilha cultivável e proprietária. Pode ser coagido a&str
.str
- é (agora, à medida que o Rust evolui) uma sequência mutável de comprimento fixo que vive na pilha ou no binário. Você pode interagir apenasstr
como um tipo emprestado por meio de uma exibição de fatia de sequência, como&str
.Considerações de uso:
Prefira
String
se você deseja possuir ou alterar uma string - como passar a string para outro thread, etc.Prefira
&str
se você deseja ter uma exibição somente leitura de uma sequência.fonte