Ответ 1
В случае String
и Vec
они делают то же самое. В целом, однако, они не совсем эквивалентны.
Во-первых, вы должны понимать Deref
. Эта черта реализуется в случаях, когда тип логически "обертывает" несколько более низкое, более простое значение. Например, все типы "умного указателя" (Box
, Rc
, Arc
) реализуют Deref
, чтобы предоставить вам доступ к их содержимому.
Он также реализован для String
и Vec
: String
"derefs" для более простых str
, Vec<T>
derefs для более простого [T]
.
Написание *s
просто ручно вызывает Deref::deref
, чтобы превратить s
в его "более простую форму". Это почти всегда записывается &*s
, однако: хотя подпись Deref::deref
говорит, что возвращает заимствованный указатель (&Target
), компилятор вставляет вторую автоматическую дереф. Это так, что, например, { let x = Box::new(42i32); *x }
приводит к i32
, а не к &i32
.
Итак, &*s
на самом деле просто сокращенно для Deref::deref(&s)
.
s[..]
представляет собой синтаксический сахар для s.index(RangeFull)
, реализованный с помощью Index
. Это означает срезать "весь диапазон" проиндексируемой вещи; для String
и Vec
, это дает вам фрагмент всего содержимого. Опять же, результат технически является заимствованным указателем, но Rust auto-derefs этот тоже, поэтому он также почти всегда записывается &s[..]
.
И какая разница? Держи эту мысль; расскажите о цепочке Deref
.
Чтобы взять конкретный пример, потому что вы можете просмотреть String
как str
, было бы очень полезно, чтобы все доступные методы str
были доступны также на String
. Вместо того, чтобы наследовать, Rust делает это цепочкой Deref
.
Как это работает, когда вы запрашиваете конкретный метод для значения, Rust сначала рассматривает методы, определенные для этого конкретного типа. Скажем, он не находит способ, который вы просили; перед тем как отказаться, Rust проверит реализацию Deref
. Если он находит один, он вызывает его, а затем повторяет попытку.
Это означает, что когда вы вызываете s.chars()
, где s
является String
, на самом деле происходит то, что вы вызываете s.deref().chars()
, потому что String
не имеет метода под названием chars
, но str
делает (прокрутите вверх, чтобы увидеть, что String
получает этот метод только потому, что он реализует Deref<Target=str>
).
Возвращаясь к исходному вопросу, разница между &*s
и &s[..]
заключается в том, что происходит, когда s
не просто String
или Vec<T>
. Возьмем несколько примеров:
-
s: String
;&*s: &str
,&s[..]: &str
. -
s: &String
:&*s: &String
,&s[..]: &str
. -
s: Box<String>
:&*s: &String
,&s[..]: &str
. -
s: Box<Rc<&String>>
:&*s: &Rc<&String>
,&s[..]: &str
.
&*s
только когда-либо удаляется один слой косвенности. &s[..]
удаляет все из них. Это связано с тем, что ни одна из Box
, Rc
, &
и т.д. Не реализует черту Index
, поэтому цепочка Deref
вызывает вызов s.index(RangeFull)
для цепочки через все эти промежуточные уровни.
Какую пользу вы должны использовать? Какой бы вы ни хотели. Используйте &*s
(или &**s
или &***s
), если вы хотите точно определить, сколько слоев косвенности вы хотите снять. Используйте &s[..]
, если вы хотите отключить их и просто получить самое внутреннее представление значения.
Или вы можете делать то, что я делаю, и использовать &*s
, потому что он читает слева направо, тогда как &s[..]
читает слева направо налево и снова меня раздражает.:)
Добавление
- Существует связанная с этим концепция
Deref
принуждений. - Там также
DerefMut
иIndexMut
, которые делают все вышеперечисленное, но для&mut
вместо&
.