Genel bir tür yerine ilişkili bir türü kullanmak ne zaman uygundur?


109

Gelen bu soruya bir sorun olduğunu ilişkili türü içine bir genel tür parametresi kullanarak bir girişim değiştirerek çözülebilir ortaya çıktı. Bu, "Neden ilişkili bir tür burada daha uygun?" Sorusunu doğurdu ve bu da beni daha fazlasını öğrenmek istememe neden oldu.

Türlerini ilişkili sunduk RFC diyor ki:

Bu RFC, özellik eşleştirmesini şu şekilde açıklar:

  • Tüm özellik türü parametrelerini şu şekilde işleme girdi türleri olarak işleme ve
  • Çıktı türleri olan ilişkili türlerin sağlanması .

RFC, motive edici bir örnek olarak bir grafik yapısı kullanır ve bu da dokümantasyonda kullanılır , ancak ilişkili tip versiyonun tip parametreli versiyona göre faydalarını tam olarak takdir etmediğimi kabul edeceğim. Öncelikle, distanceyöntemin Edgetürü önemsemesine gerek olmamasıdır . Bu güzel, ancak ilişkili türlere sahip olmanın biraz sığ bir nedeni gibi görünüyor.

İlişkili türleri pratikte kullanmanın oldukça sezgisel olduğunu buldum, ancak bunları kendi API'mde nerede ve ne zaman kullanmam gerektiğine karar verirken kendimi zorlanırken buluyorum.

Kod yazarken, genel bir tür parametresi yerine ilişkili bir türü ne zaman seçmeliyim ve bunun tersini ne zaman yapmalıyım?

Yanıtlar:


76

Buna artık The Rust Programming Language'in ikinci baskısında değiniliyor . Ancak ek olarak biraz da dalalım.

Daha basit bir örnekle başlayalım.

Bir özellik yöntemini kullanmak ne zaman uygundur?

Geç bağlama sağlamanın birden çok yolu vardır :

trait MyTrait {
    fn hello_word(&self) -> String;
}

Veya:

struct MyTrait<T> {
    t: T,
    hello_world: fn(&T) -> String,
}

impl<T> MyTrait<T> {
    fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;

    fn hello_world(&self) -> String {
        (self.hello_world)(self.t)
    }
}

Herhangi bir uygulama / performans stratejisini göz ardı ederek, yukarıdaki her iki alıntı da kullanıcının dinamik bir şekilde nasıl hello_worlddavranması gerektiğini belirtmesine izin verir .

Tek fark (anlamsal) olmasıdır traitverilen bir tür için bu uygulama garanti Tuygulayan trait, hello_worldhep oysa aynı davranışı sahip olacakstruct uygulaması örneği bazında farklı bir davranış sahip tanır.

Bir yöntemi kullanmanın uygun olup olmadığı, kullanım durumuna bağlıdır!

İlişkili bir türü kullanmak ne zaman uygundur?

traitYukarıdaki yöntemlere benzer şekilde , ilişkili bir tür (derlemede meydana gelse de) geç bağlanmanın bir biçimidir ve kullanıcının traitbelirli bir örnek için hangi türün ikame edeceğini belirtmesine izin verir . Tek yol bu değil (dolayısıyla soru):

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

Veya:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

Yukarıdaki yöntemlerin geç bağlanmasına eşdeğerdir:

  • birincisi, bir verilen Selfiçin tek birReturn ilişkili
  • İkincisi ise, bunun yerine, uygulamaya olanak MyTraitiçin Selfbirden içinReturn

Hangi formun daha uygun olduğu, birliği uygulamanın mantıklı olup olmadığına bağlıdır. Örneğin:

  • Deref ilişkili bir tür kullanır çünkü teklik olmazsa derleyici çıkarım sırasında çıldırır
  • Add ilişkili bir türü kullanır çünkü yazarı, iki bağımsız değişken verildiğinde mantıksal bir dönüş türü olacağını düşünür.

Görebileceğiniz gibi, Derefbariz bir kullanım durumu (teknik kısıt) olsa da, durum Adddaha az nettir: belki de içeriğe bağlı olarak i32 + i32ya i32da bunlardan birini vermek mantıklı Complex<i32>olabilir mi? Bununla birlikte, yazar kendi kararlarını kullandı ve eklemeler için iade türünün aşırı yüklenmesinin gereksiz olduğuna karar verdi.

Benim kişisel duruşum, doğru cevap olmadığı yönünde. Yine de, teklik argümanının ötesinde, ilişkili türlerin, belirtilmesi gereken parametrelerin sayısını azalttıkları için özelliği kullanmayı kolaylaştırdığından bahsedeceğim, bu nedenle, düzenli bir özellik parametresi kullanmanın esnekliğinin faydalarının açık olmaması durumunda, I ilişkili bir türle başlamayı önerin.


4
Biraz basitleştirmeye çalışayım: trait/struct MyTrait/MyStructtam olarak bir impl MyTrait forveya impl MyStruct. jenerik olduğu için trait MyTrait<Return>birden çok implURL'ye izin verir . Returnherhangi bir tür olabilir. Genel yapılar aynıdır.
Paul-Sebastian Manole

2
Cevabınızı "Rust Programlama Dili"
ndekinden

"İlki, belirli bir Benlik için ilişkili tek bir Geri Dönüş olduğunu zorlar". Bu, dolaysız anlamda doğrudur, ancak kuşkusuz, genel bir özellik ile alt sınıflandırma yaparak bu kısıtlamanın üstesinden gelinebilir. Belki olmakadır sadece bir öneri olabilir ve olamaz zorlanan
joel

37

İlişkili türler bir gruplama mekanizmasıdır , bu nedenle türleri birlikte gruplamak mantıklı olduğunda kullanılmalıdır.

GraphBelgelerinde ortaya özellik, bu bir örneğidir. GraphA'nın genel olmasını istersiniz , ancak belirli bir türünüz olduğunda Graph, Nodeveya Edgetürlerinin artık değişmesini istemezsiniz . Bir kişi Graphbu türleri tek bir uygulamada değiştirmek istemeyecektir ve aslında onların her zaman aynı olmasını ister. Bir araya toplanmışlar veya hatta birinin ilişkili olduğu söylenebilir .


5
Anlaması biraz zaman aldı. Bana göre daha çok aynı anda birkaç türü tanımlamak gibi görünüyor: Kenar ve Düğüm grafikten bir anlam ifade etmiyor.
tafia
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.