Сегодня мы рассмотрим управление копированием в Rust. Это нужно не только для упрощения архитектуры и снижения безопасности, но также это нужно для правильной работы копирования в ситуациях, когда это необходимо. Так же как в C++ есть конструктор перемещения, в Rust есть trait (интерфейс) копирования, который позволяет явно или не явно производить копирование ресурсов. Обращаю ваше внимание, что все сказанное далее будет относиться к пользовательским типам. Также смотрите первую часть статьи, предыдущий пост.
Первичным является имплементация интерфейса Clone для явного копирования, это позволяет нам копировать ресурсы например вот так let y = x.clone(); В этом выражении явно указано, что мы копируем, и это облегчает чтение и понимание кода. Без .clone() будет происходить перемещение. Но мы можем пойти еще дальше, и имплементировать интерфейс Copy, который в свою очередь требует наличия Clone. Тогда в let y = x; копирование будет происходить не явно вместо перемещения и .clone() не требуется. Более того в простых ситуациях, мы можем не имплементировать эти интерфейсы самостоятельно, а наследовать их с помощью атрибута #[derive(Clone, Copy)]. И все будет работать как описано выше. Смотрим пример.
point.rs
--------
//!Custom type Point
///attributes
#[derive(Debug)]
//#[derive(Clone)]
//#[derive(Copy)]
///Fields
pub struct Point {
x: i32,
y: i32,
}
///Methods
impl Point {
///constructor
pub fn new() -> Self {
Point { x: 0, y: 0 }
}
pub fn add(&self, n: i32) -> Self { //copying i32 to
Point { x: self.x + n, y: self.y + n } //moving Point from
}
pub fn sum(&self, p: Point) -> Self{ //moving Point to
Point {
x: self.x + p.x,
y: self.y + p.y
} //moving Point from
}
}
///implement trait Clone
impl Clone for Point{
fn clone(&self) -> Self {
Point{
x: self.x,
y: self.y
}
}
}
///implement trait Copy
impl Copy for Point{ }
main.rs
-------
#![allow(unused_variables, unused_imports, dead_code)]
mod point;
use point::Point;
fn plus(a: i32, b: i32) -> i32 { //copying to
a + b //copying from
}
fn main() {
//ownership basic types
let x: i32 = 5; //assign
let y: i32 = x; //copying assign
println!("{}", x); //x after copying
let z = plus(x, y); //copying to and from fn
println!("{} + {} = {}", x, y, z); //OK!!!
//ownership custom types
let p1 = Point::new(); //construct
let p2 = p1; //p1 moving assign by default
println!("{:?}", p1); //OK!!!
let p3 = p2.add(z); //copy i32 to method, moving Point from method
println!("{:?} + {} = {:?}", p2, z, p3); //OK!!!
let p4 = p2.sum(p3); //move p3 to method
println!("{:?}", p3); //OK!!!
//attribute #[derive(Clone)] for Point
//let p4 = p3.sum(p3.clone()); //clone explicit
//println!("{:?}", p4); //OK!!!
//attribute #[derive(Copy)] for Point
//println!("{:?}", p1); //OK!!! after implicit copy in: let p2 = p1;
}
Тип String из стандартной библиотеки Rust, имплементирует только интерфейс Clone, поэтому может быть либо перемещен, либо явно скопирован с помощью .clone()
fn main() {
let mut my_string = String::from("Hello, world!");
foo(my_string.clone()); //COPY
println!("{} ", my_string); //OK!
foo(my_string); //MOVE
//println!("{} ", my_string); //ERROR!
}
fn foo(s: String) -> String {
println!("{} ", s);
s
}
Если мы хотим клонировать вектор пользовательских типов (Vec<Point>), то должны либо сами имплементировать интерфейс Clone, либо наследовать его с помощью атрибута. Для вектора базовых типов (Vec<i32>) этого делать не нужно.
#[derive(Debug, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 0, y: 0 };
let p2 = Point { x: 5, y: 5 };
let points = vec![p1.clone(), p2];
foo(points.clone());
println!("points: {:?} ", points); //OK
println!("p1: {:?} ", p1); //OK
//println!("p2: {:?} ", p2); //ERROR
}
fn foo(v: Vec<Point>) {
println!("points: {:?} ", v);
}
Перегуд В.
Комментариев нет:
Отправить комментарий