Önsöz : Bu cevap, yerleşik özellikler --özel olarak Copy
yönler - uygulanmadan önce yazılmıştır . Yalnızca eski şemaya (soru sorulduğunda uygulanan) uygulanan bölümleri belirtmek için blok alıntılar kullandım.
Eski : Temel soruyu cevaplamak için, bir NoCopy
değer depolayan bir işaret alanı ekleyebilirsiniz . Örneğin
struct Triplet {
one: int,
two: int,
three: int,
_marker: NoCopy
}
Bunu bir yıkıcıya sahip olarak da yapabilirsiniz ( Drop
özelliği uygulayarak ), ancak yıkıcı hiçbir şey yapmıyorsa işaretçi türlerini kullanmak tercih edilir.
Türler artık varsayılan olarak taşınır, yani yeni bir tür tanımladığınızda, türünüz için Copy
açıkça uygulamadıkça uygulanmaz:
struct Triplet {
one: i32,
two: i32,
three: i32
}
impl Copy for Triplet {}
Uygulama ancak her tür yenide yer alıyorsa struct
veya enum
kendisiyse var olabilir Copy
. Değilse, derleyici bir hata mesajı yazdıracaktır. Ayrıca, yalnızca türün bir uygulaması yoksa var olabilir Drop
.
Sormadığınız soruyu yanıtlamak için ... "hareketler ve kopyalamada ne var?":
Öncelikle iki farklı "kopya" tanımlayacağım:
- bir bayt kopyası , bir nesneyi bayt-bayt olarak basit bir şekilde kopyalayan, işaretçileri takip etmeyen bir bayt kopyası , örneğin eğer varsa
(&usize, u64)
, 64-bit bir bilgisayarda 16 bayttır ve yüzeysel bir kopya, bu 16 baytı alıp kopyalar. bellek başka bir 16-bayt öbek değer, olmadan temas usize
diğer ucunda &
. Yani, aramaya eşdeğerdir memcpy
.
- Bir semantik kopya bir değer çoğaltarak, güvenli bir şekilde eski bir ayrı ayrı kullanılabilen bir yeni (biraz), bağımsız bir örneğini oluşturmak için. Örneğin, bir ifadenin anlamsal bir kopyası
Rc<T>
, sadece referans sayısını artırmayı içerir ve bir semantik kopyası, Vec<T>
yeni bir tahsis oluşturmayı ve daha sonra depolanan her bir öğeyi eskiden yeniye semantik olarak kopyalamayı içerir. Bunlar olabilir derin kopyalar (örn Vec<T>
) veya sığ (örn Rc<T>
dokunmuyor saklı T
), Clone
işin en küçük miktarı semantik türünde bir değer kopyalamak için gerekli olan gevşek tanımlanır T
bir içeriden &T
için T
.
Rust, C gibidir, bir değerin her by-değer kullanımı bir bayt kopyasıdır:
let x: T = ...;
let y: T = x;
fn foo(z: T) -> T {
return z
}
foo(y)
T
Hareket etsin veya taşınmasın bayt kopyalarıdır veya "örtük olarak kopyalanabilir ". (Açık olmak gerekirse, çalışma zamanında kelimenin tam anlamıyla bayt bayt kopyalar olması gerekmez: derleyici, kodun davranışı korunursa kopyaları optimize etmekte özgürdür.)
Bununla birlikte, bayt kopyalarında temel bir sorun vardır: bellekte yinelenen değerler elde edersiniz; bu, yıkıcıları varsa çok kötü olabilir, örn.
{
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
}
Eğer w
sadece düz bir bayt kopyası v
olsaydı, aynı tahsisi gösteren iki vektör olurdu, her ikisi de onu serbest bırakan yıkıcılara sahiptir ... çift serbestliğe neden olur , ki bu bir problemdir. NB. Bunun anlamsal bir kopyasını v
içine yaparsak, bu tamamen iyi olurdu w
, çünkü o w
zaman kendi bağımsız olacak Vec<u8>
ve yıkıcılar birbirlerini çiğnemeyecekti.
Burada birkaç olası düzeltme var:
- Bırakın, programcı C gibi işlesin (C'de yıkıcı yok, bu yüzden o kadar kötü değil ... bunun yerine sadece bellek sızıntıları kalıyor.: P)
w
Kopyalama yapıcılarıyla C ++ gibi kendi ayırmasına sahip olması için dolaylı olarak anlamsal bir kopya gerçekleştirin .
- Bir mülkiyet devri olarak değer kullanımlarına bakıldığında,
v
artık kullanılamaz ve yıkıcı çalıştırılamaz.
Sonuncusu Rust'un yaptığı şeydir: bir hareket , kaynağın statik olarak geçersiz kılındığı bir değerde kullanımdır, bu nedenle derleyici artık geçersiz olan belleğin daha fazla kullanılmasını engeller.
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
println!("{}", v);
Yıkıcıları olan türler , bazı kaynakların yönetimine / sahipliğine sahip olduklarından (örneğin, bir bellek tahsisi veya bir dosya tanıtıcısı) ve bir bayt kopyasının bunu doğru bir şekilde çoğaltması çok düşük bir ihtimal olduğundan, değer başına (bayt kopyalandığında) kullanıldığında hareket etmelidir. mülkiyet.
"Peki ... örtük kopya nedir?"
İlkel bir türü düşünün u8
: Bir bayt kopyası basittir, sadece tek baytı kopyalayın ve anlamsal bir kopya da aynı basittir, tek baytı kopyalayın. Özellikle, bir bayt kopyası olan bir semantik kopya ... Pas bile sahip bir dahili özelliğiCopy
olduğunu tipleri aynı semantik ve bayt kopyaları yakalar.
Bu nedenle, bu Copy
türler için değer bazında kullanımlar otomatik olarak anlamsal kopyalardır ve bu nedenle kaynağı kullanmaya devam etmek tamamen güvenlidir.
let v: u8 = 1;
let w: u8 = v;
println!("{}", v);
Eski : NoCopy
işaretleyici edilebileceğini türlerini varsayarak derleyicinin otomatik davranışını geçersiz kılar Copy
(yani sadece ilkel toplulukları ihtiva ve &
) vardır Copy
. Ancak bu, isteğe bağlı yerleşik özellikler uygulandığında değişecektir .
Yukarıda belirtildiği gibi, isteğe bağlı yerleşik özellikler uygulanmıştır, bu nedenle derleyici artık otomatik davranışa sahip değildir. Bununla birlikte, geçmişte otomatik davranış için kullanılan kural, uygulanmasının yasal olup olmadığını kontrol etmek için aynı kurallardır Copy
.