Важно понимать, что происходит с &s[1..5]
, и что это &(s[1..5])
, а именно, s[1..5]
сначала оценивается, это возвращает значение типа str
, и берется ссылка на это значение. Фактически, здесь даже больше косвенности: x[y]
в ржавчине на самом деле синтаксис c сахар для *std::ops::Index::index(x,y)
. Обратите внимание на разыменование, так как эта функция всегда возвращает ссылку, которая затем разыменовывается сахаром, а затем на нее снова ссылается &
в нашем коде - естественно, компилятор оптимизирует это и гарантирует, что мы не будем бессмысленно брать ссылки только разыменовать их снова.
Так получилось, что тип String
действительно поддерживает черту Index<Range<usize>>
, а тип Index::output
- str
.
Также бывает, что Тип str
поддерживает то же самое, и этот тип вывода также str
, viā общая реализация SliceIndex
.
Что касается вашего вопроса об автоматическом разыменовании, это правда, что в Rust есть черта Deref
, определенная в String
, так что во многих контекстах, таких как этот, &String
автоматически приводится к &str
- любой контекст, который принимает &str
, также принимает &String
, что означает, что реализация на Index<usize>
на String
на самом деле предназначена для оптимизации, чтобы избежать этого косвенного обращения. Если бы его не было, код все равно работал бы, и, возможно, компилятор мог бы даже оптимизировать косвенное обращение. много разных типов.
Наконец:
Я подумал, что правильным способом добиться этого будет что-то вроде & ((* str) [0..i]).
Это не будет работать независимо, &str
не то же самое, что &String
, и не может быть разыменован на String
как &String
. Фактически, &str
во многих отношениях ближе к String
, чем к &String
. a &str
на самом деле просто жирный указатель на последовательность байтов Unicode, который также содержит длину указанной последовательности во втором слове; a String
- это, если угодно, сверхжирный указатель, который также содержит текущую емкость буфера с ним и владеет буфером, на который он указывает, поэтому он может удалить и изменить его размер.