Enquanto aprendia rust
, encontrei este trecho incorporado em uma resposta a um problema online. Eu não esperava que essa fosse a resposta correta, pois ingenuamente pensei que elem_refs.sort()
classificaria pelas referências no vetor em vez das strings.
Por que elem_refs.sort() classifica as strings em vez das referências às strings (ou melhor, &str
's)? Obviamente, é conveniente e o resultado desejado, mas onde esse comportamento está documentado? Muito obrigado por seus insights.
fn main() {
let list = ["b", "d" , "q", "a", "t" ];
let mut elem_refs: Vec<&&str> = list.iter().collect();
println!("{:?}", elem_refs);
elem_refs.sort();
println!("{:?}", elem_refs);
}
["b", "d", "q", "a", "t"]
["a", "b", "d", "q", "t"]
Primeiro,
sort
requer o traitOrd
.Ord
é implementado para referências aqui , o que desreferencia os argumentos e chamaOrd::cmp
novamente:Quando você chama
sort
, o compilador procura uma implementação deOrd
for&&str
. Ele encontra o genérico acima, atribui&str
aA
e, de acordo com o limiteA: Ord
, procura uma implementação deOrd
for&str
. Ele encontra o mesmo novamente, atribuistr
aA
e procura uma implementação deOrd
forstr
. Ele encontra a implementação específica parastr
here , o que encerra a pesquisa. Esta cadeia de implementações é usada para gerar suasort
função específica.No meu entender, existem duas respostas para esta questão: a “filosófica” e a prática.
A resposta "filosófica" é que Rust prefere lidar com dados, e não com ponteiros. Mesmo que você tenha uma referência a um valor, muitas vezes ela é tratada apenas como um identificador para o valor, e o fato de que nos bastidores uma referência é apenas um ponteiro, com um endereço específico na memória, é um detalhe que Rust prefere que você não pense demais. (Pelo menos, este é o meu entendimento da filosofia Rust. Sinta-se à vontade para me corrigir)
Mesmo se você tiver duas referências, compará-las usando
==
as compararia por valor (devido a uma chamada paraPartialEq::eq
). Se você realmente deseja compará-los por referência, primeiro será necessário convertê-los em ponteiros ou chamar uma função comostd::ptr::addr_eq
. (E, no meu entender, até mesmo a ideia de comparação por referência é considerada não-idiomática em Rust)Agora, a resposta prática é que a implementação de
sort
usa aOrd
característica para decidir a ordem dos elementos na fatia classificada. ( veja awhere
cláusula na assinatura desort
)A
Ord
característica (e aPartialOrd
característica relacionada) existe com o propósito de decidir a ordem dos tipos por valor e, de fato, a implementação deOrd
chamadasstr
forself.as_bytes().cmp(other.as_bytes())
para comparar os bytes reais contidos nas strings.Um pouco relacionado a isso está o comportamento do operador ponto (
.
) no Rust, que é usado para acessar os campos e métodos de um tipo. Usar o operador ponto em uma variável sempre desreferencia implicitamente essa variável se for uma referência, antes de realmente acessar o campo/método.Ele irá até desreferenciar várias vezes se for uma referência aninhada. Isso significa que para qualquer tipo
T
(mesmo tipos primitivos comoi32
), se você tiver uma variávellet my_var: &&&T = ...
e fizer algo comomy_var.some_field
, isso seria equivalente a(***my_var).some_field
.Isso, por sua vez, significa que qualquer método chamado em uma referência (como os métodos
Ord
chamados pela implementação desort
), lidará com os dados reais dentro da referência, e não com a referência em si.Uma isenção de responsabilidade que gostaria de acrescentar é que sou um pouco novo no Rust e, portanto, nem tudo o que disse pode estar 100% correto. Estou aberto a comentários sobre esta resposta me corrigindo, para que eu possa editar esta resposta com as correções.