#design-patterns #rust #borrow-checker
#шаблоны проектирования #Ржавчина #проверка заимствования
Вопрос:
Рассмотрим следующую структуру данных:
struct ReferenceHoldingStruct<'a> {
pub prop: amp;'a str
}
Этот шаблон часто полезен, особенно в анализаторах (мой вариант использования), для «предоставления» структуре некоторой части строковых данных без их перераспределения:
fn generate_these_1<'a>(input: amp;'a str) -> ReferenceHoldingStruct<'a> {
ReferenceHoldingStruct { prop: input }
}
Однако у меня есть случай, когда у меня есть структура, подобная приведенной выше, но в одном месте мне нужно сгенерировать экземпляры, которые являются «независимыми»; т. Е. Они владеют своими собственными данными:
fn generate_these_2<'a>(input: String) -> ReferenceHoldingStruct<'a> {
ReferenceHoldingStruct { prop: input.as_str() }
}
Я понимаю, почему эта версия не работает: строка, на которую ссылаются, не находится нигде, где Rust может видеть, что она будет зависать, чтобы продолжать выполнять amp;str
ссылку на структуру. Я подумал, может быть, это сработает:
fn generate_these_2<'a>(input: String) -> (String, ReferenceHoldingStruct<'a>) {
(input, ReferenceHoldingStruct { prop: input.as_str() })
}
потому что, по крайней мере, строка не будет немедленно удалена. Я подумал, что, возможно, Rust мог бы выяснить, что этот кортеж содержит как ссылку, так и данные, на которые он ссылается, и что поэтому они будут действительны до тех пор, пока они хранятся вместе, как это. Но без кубиков: это все равно не работает. Он обрабатывает ссылку как заимствование, а переход к кортежу — как перемещение, и вы не можете сделать оба «одновременно».
Итак, я понимаю проблему, но не знаю, куда идти дальше. Какова стандартная практика для такого рода ситуаций?
Комментарии:
1. Не могли бы вы использовать Cow ?
2. Я могу ошибаться, но я не думаю, что Cow имеет значение, потому что это вопрос времени жизни, а не изменчивости
3. @brundolf Cow — это не только изменчивость, это тип, который может содержать собственную или ссылочную версию типа, что именно то, что вы ищете. Хотя для этого требуется, чтобы ссылка была конвертируемой в принадлежащий тип, strs будет работать из коробки.
4. Ваша последняя версия не работает, потому что Rust ожидает, что значения всегда могут быть сохранены во время перемещения. Для других случаев есть несколько типов выводов, но лучше избегать выводов, если в этом нет необходимости, потому что это усложняет код.
Ответ №1:
Вот пример с Cow
(упомянутый в комментариях):
use std::borrow::Cow;
struct ReferenceHoldingStruct<'a> {
pub prop: Cow<'a, str>
}
fn generate_these_1<'a>(input: amp;'a str) -> ReferenceHoldingStruct<'a> {
ReferenceHoldingStruct { prop: Cow::Borrowed(input) }
}
fn generate_these_2<'a>(input: String) -> ReferenceHoldingStruct<'a> {
ReferenceHoldingStruct { prop: Cow::Owned(input) }
}
Ответ №2:
Вместо этого вы могли бы хранить что-то, что реализует Borrow<str>
:
struct ReferenceHoldingStruct<S: Borrow<str>> {
pub prop: S,
}
Привязка S
позволяет использовать все, что может быть заимствовано как amp;str
. Это может быть amp;str
само по себе или принадлежащее String
(или что-нибудь еще, что реализует Borrow<str>
). Поскольку существуют общие имплы, которые реализуются Borrow<T>
на обоих T
и amp;T
, этот подход можно использовать либо для хранения ссылки, либо для владения значением любого типа, а не только str
.
Комментарии:
1. Вау. Это действительно круто и может сработать. Я попробую завтра.
2.
Borrow<T>
это здорово, но вы можете обнаружить, что ваш вариант использования лучше подходитCow<str>
, если все, что вам нужно, это «либо строка, которую я заимствую, либо строка, которой я владею»3. @JMAA ты правильно подметил.
Cow
это более простая замена, если вы используетеClone
такие типы, какstr
/String
, но этотBorrow
подход, в отличиеCow
от, работает для не-Clone
типов.