2016-03-25 6 views
2

Ben bir tamsayı bir başvuru alır ve bu tamsayı süreleri 2, 5 kez bir vektör döndüren bir işlev yazıyorum.Borç denetleyicisi sorunları olmadan bir işlevden yeni veriler referans olarak nasıl gönderilir?

fn foo(x: &i64) -> Vec<&i64> { 
    let mut v = vec![]; 
    for i in 0..5 { 
     let q = x * 2; 
     v.push(&q); 
    } 
    v 
} 

fn main() { 
    let x = 5; 
    let q = foo(&x); 
    println!("{:?}", q); 
} 

ariyet denetleyicisi ben yeni bir değişken tanımlamak, bunun nedeni yığın tahsis ve işlevi sonunda kapsam dışına gider oluyor delirdi: Böyle bir şey görünür müyüm.

ne yapmalıyım? Kesinlikle yeni veriler yaratan fonksiyonlar yazmadan hayattan geçemiyorum! Orada Box, ve Copy tipi geçici çözümler farkında değilim, ama bir deyimsel Pas çözümü ilgileniyorum.

Ben Vec<i64> geri dönebilirler fark ama aynı konularda çalıştırmak sandın? Genel olarak genel bir sorun için "sembolik" bir problem ile gelmeye çalışıyorum :)

+0

* ama bence aynı sorunların üstesinden gelebilir * - bunu denediniz mi? Neden aynı sorunlara sahip olacağını düşünüyorsun? – Shepmaster

+0

@Shepmaster I64'leri yeni işlevde, yığınta ayırırsam, kızarırdım. Ama öyle görünüyor ki çalışıyor. Sanırım bunun sebebi arayanı tekrar arayan kişiye verdiğim için, borç kontrolörü üzülmüyor, yani borç yok mu? Bu tür bir çözüm, Kutu veya Hücre gibi bir yığın-ayrılmış yapıyı kullanmaktan daha idiomatik midir? – William

+1

Borcunuz olmadığı konusunda haklısınız: 'i64'leri yığına ayırın, daha sonra bunları' Vec'e aktarın ve daha sonra 'Vec'in mülkiyetini tekrar arayan kişiye aktarın. * içeren bir yığın-ayrılmış yapısını kullanarak daha idiomatik * - bir 'Vec' ** **" bir yığın-ayrılmış yapıya sahip "dir. Bu durumda bir 'Vec ''' Box' veya 'Cell' kullanmaktan daha aptalca olduğunu söyleyebilirim. Bu özel örnekte, bir dizi de döndürebilirsiniz '[i64; 5] 'bildiğinizden, kaç tane geri dönüş yapacağınızı biliyorsunuz. – Shepmaster

cevap

4

DÜZENLEME: Sadece yazdığınızı fark ettim: "Kutu, Kopya vb. Geçici bir çözüm olduğunun farkındayım ama çoğunlukla ilgileniyorum deyimsiz pas çözümü ", ama ben bütün cevabı zaten yazdım. : P Ve 'un altındaki çözümler, deyimsel Rust'tur, bu, hafızanın işleyiş şeklidir! C veya C++'da stack-allocated verilere işaretçiler döndürmeye çalışmayın çünkü derleyici sizi durdurmasa bile, bu iyi bir şey gelmeyecektir anlamına gelmez. ;)


bir referans dönüş her zaman, bu referans şart işleve bir parametre olmuştur. Eğer verilere başvurular dönen eğer Başka bir deyişle, bütün bu verilerin fonksiyonunun dışında tahsis edilmiş olmalıdır. Bunu anlıyor gibisin, sadece açık olduğundan emin olmak istiyorum. :) Kullanım durumunuz ne bağlı bu sorunu çözme birçok potansiyel yolu vardır

bulunmaktadır. Bu özel örnekte

, sen, sadece hiç referanslarla rahatsız etmeden foo sahiplik verebilir sonra herhangi bir şey için x gerekmediğinden:

fn foo(x: i64) -> Vec<i64> { 
    std::iter::repeat(x * 2).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(x)); 
} 

Ama İstemediğiniz diyelim sahipliğini foo'a geçirmek için. Sen hala sürece temel değeri mutasyona istemiyordu olarak referansların bir vektör geri dönebilirler:

fn foo(x: &i64) -> Vec<&i64> { 
    std::iter::repeat(x).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

... ve aynı şekilde sizin sürece istemediğini olarak temel değeri mutasyona olabilir Yeni işaretçileri dağıtmak için:

... ama elbette ikisini de yapmak istiyorsunuz. Yani eğer hafıza ayırıyorsanız ve geri vermek istiyorsanız, onu yığından başka bir yerde yapmanız gerekir. Box kullanarak sadece yığın üzerinde şeyler yapabileceğim bir şey,:

// Just for illustration, see the next example for a better approach 
fn foo(x: &i64) -> Vec<Box<i64>> { 
    std::iter::repeat(Box::new(x * 2)).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

... Yukarıda ile Sadece yığını kullanarak genel bir araç olarak Box farkındayız emin olmak olsa .Gerçekten, basitçe bir Vec kullanarak veri yığınının üzerine yerleştirilmiş olacağı anlamına gelir, bu nedenle bu işleri:

fn foo(x: &i64) -> Vec<i64> { 
    std::iter::repeat(x * 2).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

yukarıdaki gibi hiç bir kullanım durumu farklı bir şey talep edebilecek olsa burada muhtemelen en deyimsel örneğidir.

Alternatif olarak, C'nin kitabından, bir hileyi çekebilirdi ve foo ait dışında hafızayı önceden tahsis ve sonra buna bir referans geçmesi:

fn foo(x: &i64, v: &mut [i64; 5]) { 
    for i in v { 
     *i = x * 2; 
    } 
} 

fn main() { 
    let x = 5; 
    let mut v = [0; 5]; // fixed-size array on the stack 
    foo(&x, &mut v); 
    println!("{:?}", v); 
} 

Son olarak fonksiyonu zorunluluk eğerve parametrelerini referans olarak almak için, referans verisini ve mutasyona tabi tutmalısınız, referansın kendisini kopyalamanız gerekir ve bu kopyalanmış referansları geri göndermelisiniz, daha sonrakullanabilirsiniz Bunun için:

use std::cell::Cell; 

fn foo(x: &Cell<i64>) -> Vec<&Cell<i64>> { 
    x.set(x.get() * 2); 
    std::iter::repeat(x).take(5).collect() 
} 

fn main() { 
    let x = Cell::new(5); 
    println!("{:?}", foo(&x)); 
} 

Cell, verimli ve non-şaşırtıcı hem nota rağmen sadece (bütün temel sayısal türleri yapmak) Copy özelliği uygulamak türlerinde Cell çalışır. Yazıcınız Copy'u uygulamıyorsa, RefCell ile aynı şeyi yapabilirsiniz, ancak hafif bir çalışma süresi yükü yükler ve "borçlanmayı" yanlış alırsanız çalışma zamanında panik olasılığını açar.

+0

Bunun için çok teşekkür ederim. Bunu milyonlarca kez daha net bir hale getirir ve deli olmadığımı bilmekten memnun olurum! Kutunun gibi şeylerden kaçınmanın çoğu zaman düşünülmüş gibi geldiğini düşünmüştüm ama eğer hiçbir yükü yoksa, kimin umurunda ki? Tek soru şu ki, bir Vec 'u i64'ün fn içinde yapılmış olması için "daha idiomatik" olarak kabul edilir mi? Yığın tahsisatını önlemek için? – William

+0

@William, re: 'Vec ' en deyimsel yaklaşım, cevabı evet, ve bunu yansıtmak için yukarıdakileri güncelledim. 'Box'u kullanarak benim örneğim, öbeği kullanmanın birçok yolu olduğu açık olduğundan emin olmaktı. Eğer "yeni veriyi geri döndürmek" isteyen bir fonksiyona sahip olsaydınız ve * zaten * bir Vec'in döndüğü bir fonksiyona sahip olmadıysa, o zaman yerine 'Box' kullandı. –

+0

@William Ayrıca, 'Box'un * yok * ek yükü olduğunu söylemem çünkü bir yığın ayırma yığın destekten daha fazladır. Eğer * gerçekten * maksimum performansa ihtiyacınız varsa (ama erken optimizasyon vb. Dikkat edin), daha önceki bir yığın çerçevesinde tahsis edilen belleğe referansta geçtiğiniz C-stili yaklaşımı göz önünde bulundurmalısınız. Bunu göstermek için "Vec" yerine yığın-ayrılmış dizileri kullanmak için C stili örneğini bile güncelleyeceğim. –

İlgili konular