Подтвердить что ты не робот

Есть ли разница между нарезкой и явным переборкой при преобразовании строк в & strs?

Являются ли следующие два примера эквивалентными?

Пример 1:

let x = String::new();
let y = &x[..];

Пример 2:

let x = String::new();
let y = &*x;

Является ли более эффективным, чем другой, или они в основном одинаковы?

4b9b3361

Ответ 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 вместо &.

Ответ 2

Они абсолютно одинаковы для String и Vec.

Синтаксис [..] приводит к вызову Index<RangeFull>::index(), и это не просто сахар для [0..collection.len()]. Последний будет вводить стоимость связанной проверки. Рад, что это не так в Rust, поэтому они оба одинаково быстры.


Соответствующий код: