Являются ли следующие два примера эквивалентными?
Пример 1:
let x = String::new();
let y = &x[..];
Пример 2:
let x = String::new();
let y = &*x;
Является ли более эффективным, чем другой, или они в основном одинаковы?
Являются ли следующие два примера эквивалентными?
Пример 1:
let x = String::new();
let y = &x[..];
Пример 2:
let x = String::new();
let y = &*x;
Является ли более эффективным, чем другой, или они в основном одинаковы?
В случае 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
вместо &
.Они абсолютно одинаковы для String
и Vec
.
Синтаксис [..]
приводит к вызову Index<RangeFull>::index()
, и это не просто сахар для [0..collection.len()]
. Последний будет вводить стоимость связанной проверки. Рад, что это не так в Rust, поэтому они оба одинаково быстры.
Соответствующий код:
index
of String
deref
из String
index
of Vec
(просто возвращает self
, который запускает принуждение deref, таким образом, выполняет точно такой же код, как только deref
)deref
of Vec