Yapıları Tanımlama ve Örnekleme
Yapılar, her ikisinin de birden çok ilişkili değeri içermesi bakımından “Demet Türü” bölümünde tartışılan demetlere benzer. Demetler gibi, bir yapının parçaları farklı türleri olabilir. Demetlerden farklı olarak, bir yapı içinde, değerlerin ne anlama geldiğini netleştirmek için her bir veri parçasını adlandıracaksınız. Bu adların eklenmesi, yapıların tanımlama gruplarından daha esnek olduğu anlamına gelir: bir örneğin değerlerini belirtmek veya bunlara erişmek için verilerin sırasına güvenmeniz gerekmez.
Bir yapı tanımlamak için, struct
anahtar sözcüğünü girer ve tüm yapıyı adlandırırız. Bir yapının adı, birlikte gruplandırılan veri parçalarının önemini açıklamalıdır. Ardından süslü parantezler içinde üye dediğimiz veri parçalarının adlarını ve türlerini tanımlarız.
Örneğin, Liste 5-1, bir kullanıcı hesabı hakkında bilgi depolayan bir yapı gösterir.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() {}
Bir yapıyı tanımladıktan sonra kullanmak için, üyelerin her biri için somut değerler belirterek o yapının bir örneğini yaratırız.
Yapının adını belirterek bir örnek oluşturuyoruz ve ardından anahtarların üye adları olduğu ve değerlerin bu üyelerde depolamak istediğimiz veriler olduğu anahtar: değer çiftlerini içeren süslü parantezleri ekliyoruz.
Üyeleri struct
içinde belirttiğimiz sırayla belirtmemize gerek yok.
Başka bir deyişle, struct
tanımı, tür için genel bir şablon gibidir ve örnekler,
türün değerlerini oluşturmak için bu şablonu belirli verilerle doldurur.
Örneğin, Liste 5-2'de gösterildiği gibi belirli bir kullanıcıyı bildirebiliriz.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; }
Bir yapıdan belirli bir değer elde etmek için nokta gösterimini kullanırız.
Örneğin, bu kullanıcının e-posta adresine erişmek için user1.email
kullanıyoruz.
Örnek değişken ise, nokta gösterimini kullanarak ve belirli bir alana atayarak bir değeri değiştirebiliriz. Liste 5-3,
değiştirilebilir bir User
örneğinin email
alanındaki değerin nasıl değiştirileceğini gösterir.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { let mut user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com"); }
Tüm örneğin değiştirilebilir olması gerektiğini unutmayın; Rust, yalnızca belirli alanları değiştirilebilir olarak işaretlememize izin verme. Herhangi bir ifadede olduğu gibi, bu yeni örneği örtük olarak döndürmek için fonksiyon gövdesindeki son ifade olarak yapının yeni bir örneğini oluşturabiliriz.
Liste 5-4, verilen email
ve username
ile bir User
örneği döndüren bir build_user
fonksiyonunu gösterir.
active
üyesi true
değerini, sign_in_count
ise 1
değerini alır.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { email: email, username: username, active: true, sign_in_count: 1, } } fn main() { let user1 = build_user( String::from("someone@example.com"), String::from("someusername123"), ); }
Fonksiyon parametrelerini yapı üyeleriyle aynı adla adlandırmak mantıklıdır,
ancak email
ve username
üye adlarını ve değişkenlerini tekrarlamak biraz sıkıcıdır.
Yapının daha fazla üyesi olsaydı, her adı tekrarlamak daha da can sıkıcı olurdu.
Neyse ki, uygun bir kısayol var!
Üye Başlatıcı Kısayolu Kullanma
Parametre adları ve yapı üye adları Liste 5-4'te tamamen aynı olduğundan,
build_user
'ı yeniden yazmak için üye başlatıcı kısayolu söz dizimini kullanabiliriz,
böylece tam olarak aynı şeyi elde ederiz ve email
ve username
tekrarı olmaz. Liste 5-5'te gösterilmiştir.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } } fn main() { let user1 = build_user( String::from("someone@example.com"), String::from("someusername123"), ); }
Burada, email
adlı bir üyeye sahip olan User
yapısının yeni bir örneğini oluşturuyoruz.
email
üyesinin değerini build_user
fonksiyonunun email
parametresindeki değere atamak istiyoruz.
email
üyesi ve email
parametresi aynı ada sahip olduğundan, email: email
yerine sadece email
yazmamız gerekiyor.
Yapı Güncelleme Söz Dizimi ile Diğer Örneklerden Örnekler Oluşturma
Başka bir örnekteki değerlerin çoğunu içeren ancak bazılarını değiştiren bir yapının yeni bir örneğini oluşturmak genellikle yararlıdır. Bunu yapı güncelleme söz dizimini kullanarak yapabilirsiniz.
İlk olarak, Liste 5-6'da, güncelleme söz dizimi olmadan user2
'de düzenli olarak yeni bir User
örneğinin nasıl oluşturulacağını
gösteriyoruz. email
için yeni bir değer belirledik, ancak bunun dışında user1
'den Liste 5-2'de oluşturduğumuz aynı değerleri kullanıyoruz
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { // --snip-- let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; let user2 = User { active: user1.active, username: user1.username, email: String::from("another@example.com"), sign_in_count: user1.sign_in_count, }; }
Yapı güncelleme söz dizimini kullanarak, Liste 5-7'de gösterildiği gibi aynı etkiyi daha az kodla elde edebiliriz.
..
söz dizimi, açıkça ayarlanmayan kalan üyelerin verilen örnekteki üyelerle aynı değere sahip olması gerektiğini belirtir.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { // --snip-- let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; let user2 = User { email: String::from("another@example.com"), ..user1 }; }
Liste 5-7'deki kod ayrıca user2
'de email
için farklı bir değere sahip olan ancak user1
'den username
, active
ve sign_in_count
üyeleri için aynı değerlere sahip bir örnek oluşturur.
..user1
'de kalan üyelerin değerlerini user1
'deki karşılık gelen üyelerden alması gerektiğini belirtmek için en son gelmelidir,
ancak üyelerin sırasına bakılmaksızın herhangi bir sırayla istediğimiz kadar üye için değer belirtmeyi seçebiliriz.
Yapı güncelleme söz diziminin bir atama gibi =
kullandığını unutmayın; bunun nedeni, tıpkı “Değişkenlerin ve Veri Etkileşiminin Yolları:
Hareket Ettirme” bölümünde gördüğümüz gibi verileri hareket ettirmesidir.
Bu örnekte, user1
'in username
üyesindeki String
, user2
'ye taşındığından, user2
oluşturulduktan sonra artık
user1
'i kullanamayız. user2
'ye hem email
hem de username
için yeni String
değerleri vermiş olsaydık ve
bu nedenle yalnızca user1
'den active
ve sign_in_count
değerlerini kullansaydık, o zaman user1
, user2
oluşturulduktan sonra da
geçerli olurdu. active
ve sign_in_count
türleri, Copy
tanımını uygulayan türlerdir, bu nedenle
“Sadece Yığıtı Kullanan Tür: Copy” bölümünde tartıştığımız davranış geçerli olur.
Farklı Türler Oluşturmak için Adlandırılmış Alanlar Olmadan Demet Yapılarını Kullanma
Rust ayrıca, demet yapıları adı verilen demetlere benzeyen yapıları da destekler. Demet yapıları, yapı adının sağladığı ek anlama sahiptir, ancak alanlarıyla ilişkilendirilmiş adları yoktur; daha ziyade, sadece alanların türlerine sahiptirler. Demet yapıları, tüm demete bir ad vermek ve demeti diğer demetlerden farklı bir tür yapmak istediğinizde ve her alanı normal bir yapıdaki gibi adlandırmak ayrıntılı veya gereksiz olduğunda yararlıdır.
Bir demet yapısı tanımlamak için, struct
anahtar sözcüğü ve yapı adıyla başlayın ve ardından demetteki türleri takip edin.
Örneğin, burada Color
ve Point
adında iki demet yapısı tanımlıyor ve kullanıyoruz:
struct Color(i32, i32, i32); struct Point(i32, i32, i32); fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
black
ve origin
değerlerinin farklı türler olduğuna dikkat edin, çünkü bunlar farklı demet yapılarının örnekleridir.
Tanımladığınız her yapı, yapı içindeki üyeler aynı türlere sahip olsa bile kendine özgü türe sahiptir. Örneğin, Color
türünde bir parametre
alan bir fonksiyon, her iki tür de üç i32
değerinden oluşsa bile argüman olarak bir Point
alamaz.
Üyesiz Birim Benzeri Yapılar
Ayrıca üyesi olmayan yapılar da tanımlayabilirsiniz! Bunlara birim benzeri yapılar denir, çünkü “Demet Türü” bölümünde
bahsettiğimiz birim tipine ()
benzer şekilde davranırlar. Birim benzeri yapılar, bir tür üzerinde bir özellik uygulamanız gerektiğinde ancak türün kendisinde depolamak istediğiniz herhangi bir veriniz olmadığında faydalı olabilir. Nitelikleri Bölüm 10'da tartışacağız.
İşte AlwaysEqual
adlı bir birim yapısının bildirilmesine ve somutlaştırılmasına bir örnek:
struct AlwaysEqual; fn main() { let subject = AlwaysEqual; }
AlwaysEqual
'ı tanımlamak için struct
anahtar sözcüğünü, istediğimiz adı ve ardından noktalı virgül kullanırız.
Süslü parantezlere veya parantezlere gerek yok! Daha sonra, benzer bir şekilde konu değişkeninde AlwaysEqual
örneğini alabiliriz:
tanımladığımız adı kullanarak, herhangi bir süslü parantez kullanmadan. Daha sonra, her AlwaysEqual
örneğinin her zaman diğer herhangi bir türün her örneğine eşit olduğu, belki de test amacıyla bilinen bir sonuca sahip olacak şekilde bu tür için davranış uygulayacağımızı hayal edin. Bu davranışı uygulamak için herhangi bir veriye ihtiyacımız olmazdı! Bölüm 10'da özelliklerin nasıl tanımlanacağını ve birim benzeri yapılar da dahil olmak üzere herhangi bir türe nasıl uygulanacağını göreceksiniz.
Yapı Verilerinin Sahipliği
Liste 5-1'deki
User
yapısı tanımında,&str
dizgi dilim tipi yerine sahip olunanString
tipini kullandık. Bu bilinçli bir seçimdir çünkü bu yapının her örneğinin tüm verilerine sahip olmasını ve bu verilerin tüm yapı geçerli olduğu sürece geçerli olmasını istiyoruz.Yapıların başka bir şeye ait verilere başvuruları depolaması da mümkündür, ancak bunu yapmak için yaşam sürelerinin kullanılması gerekir; bu, Bölüm 10'da tartışacağımız bir Rust özelliğidir. Ömürler, bir yapı tarafından başvurulan verilerin aşağıdakiler için geçerli olmasını sağlar. yapı olduğu sürece. Diyelim ki aşağıdaki gibi yaşam süreleri belirtmeden bir
struct
içinde bir referans depolamaya çalışıyorsunuz; bu işe yaramaz:Dosya adı: src/main.rs
struct User { active: bool, username: &str, email: &str, sign_in_count: u64, } fn main() { let user1 = User { email: "someone@example.com", username: "someusername123", active: true, sign_in_count: 1, }; }
Derleyici, ömürlük belirteçlere ihtiyaç duyduğundan hata verecektir:
$ cargo run Compiling structs v0.1.0 (file:///projects/structs) error[E0106]: missing lifetime specifier --> src/main.rs:3:15 | 3 | username: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct User<'a> { 2 | active: bool, 3 ~ username: &'a str, | error[E0106]: missing lifetime specifier --> src/main.rs:4:12 | 4 | email: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct User<'a> { 2 | active: bool, 3 | username: &str, 4 ~ email: &'a str, | For more information about this error, try `rustc --explain E0106`. error: could not compile `structs` due to 2 previous errors
Bölüm 10'da, referansları yapılarda saklayabilmeniz için bu hataların nasıl düzeltileceğini tartışacağız, ancak şimdilik,
&str
gibi referanslar yerineString
gibi sahip olunan türleri kullanarak bu gibi hataları düzelteceğiz.