Почему свойство Copy необходимо для инициализации массива по умолчанию (struct valueed)?
Когда я определяю такую структуру, я могу передать ее функции по значению, не добавляя ничего конкретного:
#[derive(Debug)]
struct MyType {
member: u16,
}
fn my_function(param: MyType) {
println!("param.member: {}", param.member);
}
Когда я хочу создать массив MyType
экземпляры со значением по умолчанию
fn main() {
let array = [MyType { member: 1234 }; 100];
println!("array[42].member: ", array[42].member);
}
Компилятор Rust говорит мне:
error[E0277]: the trait bound `MyType: std::marker::Copy` is not satisfied
--> src/main.rs:11:17
|
11 | let array = [MyType { member: 1234 }; 100];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `MyType`
|
= note: the `Copy` trait is required because the repeated element will be copied
Когда я реализую Copy
а также Clone
, все работает:
impl Copy for MyType {}
impl Clone for MyType {
fn clone(&self) -> Self {
MyType {
member: self.member.clone(),
}
}
}
Почему мне нужно указать пустой
Copy
черта реализации?Есть ли более простой способ сделать это, или я должен что-то переосмыслить?
Почему это работает при передаче экземпляра
MyType
к функции по значению? Я предполагаю, что он перемещается, поэтому в первую очередь его нет.
2 ответа
В отличие от C/C++, Rust имеет очень явное различие между типами, которые копируются и которые перемещаются. Обратите внимание, что это только семантическое различие; на уровне реализации перемещение является мелкой побайтовой копией, однако компилятор накладывает определенные ограничения на то, что вы можете делать с переменными, из которых вы переместились.
По умолчанию каждый тип является только подвижным (не копируемым). Это означает, что значения таких типов перемещаются:
let x = SomeNonCopyableType::new();
let y = x;
x.do_something(); // error!
do_something_else(x); // error!
Вы видите, значение, которое было сохранено в x
был перемещен в y
и поэтому вы ничего не можете сделать с x
,
Семантика Move является очень важной частью концепции владения в Rust. Вы можете прочитать больше об этом в официальном руководстве.
Некоторые типы, однако, достаточно просты, поэтому их побайтная копия также является их семантической копией: если вы копируете побайтовое значение, вы получите новое полностью независимое значение. Например, примитивные числа являются такими типами. Такое свойство обозначается Copy
черта в Rust, т.е. если тип реализует Copy
, тогда значения этого типа неявно копируются. Copy
не содержит методов; он существует исключительно для того, чтобы отметить, что реализующие типы имеют определенное свойство, и поэтому его обычно называют признаком маркера (как и несколько других признаков, которые делают подобные вещи).
Тем не менее, это не работает для всех типов. Например, структуры, подобные динамически размещенным векторам, не могут быть автоматически скопированы: если бы они были, адрес содержащегося в них выделения также был бы скопирован байтами, и тогда деструктор такого вектора будет запущен дважды по одному и тому же распределению, вызывая это указатель освобождается дважды, что является ошибкой памяти.
Поэтому по умолчанию пользовательские типы в Rust не копируются. Но вы можете подписаться на это, используя #[derive(Copy, Clone)]
(или, как вы заметили, используя прямой impl
; они эквивалентны, но derive
обычно лучше читает)
#[derive(Copy, Clone)]
struct MyType {
member: u16
}
(вывод Clone
необходимо, потому что Copy
наследуется Clone
так что все что есть Copy
также должен быть Clone
)
Если ваш тип может быть автоматически копируемым в принципе, то есть он не имеет связанного деструктора и все его члены Copy
затем с derive
Ваш тип также будет Copy
,
Ты можешь использовать Copy
вводит в инициализаторе массива именно потому, что массив будет инициализирован байтовыми копиями значения, используемого в этом инициализаторе, поэтому ваш тип должен реализовать Copy
обозначить, что это действительно может быть автоматически скопировано.
Выше был ответ на 1 и 2. Что касается 3, да, вы абсолютно правы. Это работает именно потому, что значение перемещено в функцию. Если вы пытались использовать переменную MyType
введите после того, как вы передадите его в функцию, вы быстро заметите ошибку при использовании перемещенного значения.
Зачем мне нужно указывать пустую реализацию признака копирования?
Copy
это особая встроенная черта, такая, что T
реализации Copy
представляет, что безопасно дублировать значение типа T
с мелкой байтовой копией.
Это простое определение означает, что нужно просто сообщить компилятору, что семантика верна, поскольку в поведении во время выполнения нет фундаментальных изменений: оба - движение (неCopy
type) и "copy" - это мелкие байтовые копии, вопрос лишь в том, можно ли использовать источник позже. Смотрите более старый ответ для более подробной информации.
(Компилятор будет жаловаться, если содержимое MyType
не Copy
сам; ранее это было бы автоматически реализовано, но все изменилось с помощью встроенных черт.)
Создание массива дублирует значение с помощью мелких копий, и это гарантированно безопасно, если T
является Copy
, Это безопасно в более общих ситуациях, # 5244 покрывает некоторые из них, но в основном, неCopy
struct не сможет использоваться для автоматического создания массива фиксированной длины, потому что компилятор не может сказать, что дублирование является безопасным / правильным.
Есть ли более простой способ сделать это, или я должен что-то переосмыслить (я из C)?
#[derive(Copy)]
struct MyType {
member: u16
}
вставит соответствующую пустую реализацию (#[derive]
работает с несколькими другими чертами, например, часто видит #[derive(Copy, Clone, PartialEq, Eq)]
.)
Почему это работает при передаче экземпляра
MyType
к функции по значению? Я предполагаю, что он перемещается, поэтому в первую очередь его нет.
Ну, без вызова функции нельзя увидеть поведение перемещения или копирования (если бы вы вызывали ее дважды, неCopy
значение, компилятор выдаст ошибку о перемещенных значениях). Но "перемещение" и "копирование" на машине одинаковы. Все значения значения по значению являются копиями семантически в Rust, как и в C.