Примечание редактора : этот вопрос задавался до Rust 1.0, и некоторые утверждения в вопросе не обязательно верны в Rust 1.0. Некоторые ответы были обновлены с учетом обеих версий.
У меня есть эта структура
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Если я передаю это функции, она неявно копируется. Иногда я читаю, что некоторые значения не копируются и поэтому их нужно переместить.
Можно ли сделать эту структуру Triplet
не копируемой? Например, можно ли реализовать Triplet
трейт, который сделает не копируемым и, следовательно, «перемещаемым»?
Я где-то читал, что нужно реализовать Clone
черту, чтобы копировать вещи, которые не могут быть неявно копируемыми, но я никогда не читал об обратном, то есть о том, что есть что-то, что неявно копируется, и делает его некопируемым, чтобы вместо этого он перемещался.
Есть ли в этом хоть какой-то смысл?
Ответы:
Предисловие : этот ответ был написан до того, как были реализованы встроенные характеристики, в частности, аспекты . Я использовал блочные кавычки, чтобы указать разделы, которые применимы только к старой схеме (той, которая применялась, когда был задан вопрос).
Copy
Типы теперь перемещаются по умолчанию, то есть когда вы определяете новый тип, он не реализуется,
Copy
если вы явно не реализуете его для своего типа:Реализация может существовать только в том случае, если каждый тип содержится в новом
struct
илиenum
является самим собойCopy
. Если нет, компилятор выведет сообщение об ошибке. Он также может существовать только в том случае, если тип не имеетDrop
реализации.Чтобы ответить на вопрос, который вы не задавали ... «что там с ходами и копией?»:
Сначала я определю две разные «копии»:
(&usize, u64)
, это 16 байт на 64-разрядном компьютере, и неполную копию будет принимать эти 16 байт и тиражирование их значение в каком-либо другом 16-байтовом фрагменте памяти, не касаясьusize
другого конца&
. То есть это эквивалент звонкаmemcpy
.Rc<T>
включает в себя просто увеличение счетчика ссылок, а семантическая копия объектаVec<T>
включает создание нового распределения, а затем семантическое копирование каждого сохраненного элемента из старого в новый. Это могут быть глубокие копии (напримерVec<T>
) или мелкие (напримерRc<T>
, не касаются сохраненногоT
),Clone
в общих чертах определяется как наименьший объем работы, необходимый для семантического копирования значения типаT
изнутри&T
вT
.Rust похож на C, каждое использование значения по значению является байтовой копией:
Они являются байтовыми копиями независимо от того,
T
перемещаются они или нет, или "неявно копируемые". (Чтобы было ясно, они не обязательно буквально побайтовые копии во время выполнения: компилятор может оптимизировать копии, если поведение кода сохраняется.)Однако есть фундаментальная проблема с байтовыми копиями: в результате вы получаете дублированные значения в памяти, что может быть очень плохо, если у них есть деструкторы, например
Если бы это
w
была просто байтовая копия,v
тогда было бы два вектора, указывающие на одно и то же выделение, оба с деструкторами, которые его освобождают ... вызывая двойное освобождение , что является проблемой. NB. Это было бы прекрасно, если бы мы сделали семантическую копиюv
intow
, поскольку тогда онаw
была бы независимойVec<u8>
и деструкторы не топтали бы друг друга.Здесь есть несколько возможных исправлений:
w
нее было собственное выделение, как в C ++ с его конструкторами копирования.v
его больше нельзя было использовать и не запускал деструктор.Последним является то, что делает Rust: перемещение - это просто использование по значению, когда источник статически аннулируется, поэтому компилятор предотвращает дальнейшее использование недействительной памяти.
Типы, у которых есть деструкторы, должны перемещаться при использовании по значению (то есть при копировании байта), поскольку они имеют управление / владение некоторым ресурсом (например, выделение памяти или дескриптор файла) и очень маловероятно, что байтовая копия будет правильно дублировать это владение.
"Ну ... что за неявная копия?"
Подумайте о примитивном типе, например
u8
: байтовая копия проста, просто скопируйте единственный байт, а семантическая копия так же проста, скопируйте единственный байт. В частности, байтовая копия - это семантическая копия ... Rust даже имеет встроенный трейт,Copy
который фиксирует, какие типы имеют идентичные семантические и байтовые копии.Следовательно, для этих
Copy
типов использования по значению также автоматически являются семантическими копиями, и поэтому вполне безопасно продолжать использование источника.Как упоминалось выше, реализованы встроенные признаки opt-in, поэтому у компилятора больше нет автоматического поведения. Однако в прошлом для автоматического поведения использовались те же правила, что и для проверки законности реализации
Copy
.источник
Самый простой способ - встроить в свой шрифт что-то, что нельзя копировать.
Стандартная библиотека предоставляет «тип маркера» именно для этого варианта использования: NoCopy . Например:
источник