Bir modülü birkaç dosyaya bölme


104

Her biri kendi dosyasında olan birden çok yapıya sahip bir modüle sahip olmak istiyorum . MathÖrnek olarak bir modül kullanmak :

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

Her yapının aynı modülde olmasını istiyorum, bunu ana dosyamdan kullanacağım, şöyle:

use Math::Vector;

fn main() {
  // ...
}

Ancak Rust'un modül sistemi (başlangıçta biraz kafa karıştırıcı olan) bunu yapmanın açık bir yolunu sağlamaz. Görünüşe göre tüm modülünüzün tek bir dosyada olmasına izin veriyor. Bu rustik değil mi? Değilse, bunu nasıl yaparım?


1
"Her biri kendi dosyasında, birden çok yapı içeren bir modül istiyorum" şeklinde yorumladım. Bu, her yapı tanımını kendi dosyasında istediğiniz anlamına gelir.
BurntSushi5

1
Modül sistemi böyle bir yapılandırmaya kesinlikle izin verse de, bu rustik olarak kabul edilmeyecektir. Bir modül yolunun doğrudan bir dosya sistemi yoluna karşılık gelmesi genellikle tercih edilir, örneğin yapı veya foo::bar::Baziçinde tanımlanmalıdır . foo/bar.rsfoo/bar/mod.rs
Chris Morgan

Yanıtlar:


115

Rust'un modül sistemi aslında inanılmaz derecede esnektir ve kodunuzun dosyalarda nasıl yapılandırıldığını gizlerken istediğiniz türden yapıyı ortaya çıkarmanıza izin verir.

Bence buradaki anahtar pub use, diğer modüllerden tanımlayıcıları yeniden dışa aktarmanıza izin verecek şekilde yararlanmaktır. std::ioAlt modüllerden bazı türlerin kullanım için yeniden ihraçstd::io edildiği Rust'un sandığında bunun için bir emsal var .

Düzenleme (2019-08-25): Cevabın aşağıdaki kısmı oldukça uzun bir süre önce yazılmıştır. rustcTek başına böyle bir modül yapısının nasıl kurulacağını açıklar . Bugün, çoğu kullanım durumu için genellikle Cargo kullanılır. Aşağıdakiler hala geçerli olsa da, bazı kısımları (örneğin #![crate_type = ...]) tuhaf görünebilir. Bu önerilen çözüm değildir.

Örneğinizi uyarlamak için şu dizin yapısıyla başlayabiliriz:

src/
  lib.rs
  vector.rs
main.rs

İşte sizin main.rs:

extern crate math;

use math::vector;

fn main() {
    println!("{:?}", vector::VectorA::new());
    println!("{:?}", vector::VectorB::new());
}

Ve sizin src/lib.rs:

#[crate_id = "math"];
#[crate_type = "lib"];

pub mod vector; // exports the module defined in vector.rs

Ve son olarak src/vector.rs:

// exports identifiers from private sub-modules in the current
// module namespace
pub use self::vector_a::VectorA;
pub use self::vector_b::VectorB;

mod vector_b; // private sub-module defined in vector_b.rs

mod vector_a { // private sub-module defined in place
    #[derive(Debug)]
    pub struct VectorA {
        xs: Vec<i64>,
    }

    impl VectorA {
        pub fn new() -> VectorA {
            VectorA { xs: vec![] }
        }
    }
}

Ve sihrin gerçekleştiği yer burasıdır. math::vector::vector_aÖzel bir vektör türünün bazı uygulamalarını içeren bir alt modül tanımladık . Ancak kütüphanenizin müşterilerinin bir vector_aalt modül olduğunu umursamasını istemiyoruz . Bunun yerine, onu math::vectormodülde kullanıma sunmak istiyoruz . Bu, tanımlayıcıyı mevcut modülde pub use self::vector_a::VectorAyeniden dışa aktaran ile yapılır vector_a::VectorA.

Ancak özel vektör uygulamalarınızı farklı dosyalara koyabilmeniz için bunu nasıl yapacağınızı sordunuz. Bu nedir mod vector_b;hat yok. Rust derleyicisine vector_b.rs, o modülün uygulanması için bir dosya aramasını söyler. Ve tabii ki, işte src/vector_b.rsdosyamız:

#[derive(Debug)]
pub struct VectorB {
    xs: Vec<i64>,
}

impl VectorB {
    pub fn new() -> VectorB {
        VectorB { xs: vec![] }
    }
}

Müşterinin bakış açısından, gerçeği VectorAve VectorBiki farklı dosyalarda iki farklı modüllerde tanımlanan tamamen opaktır.

İle aynı dizindeyseniz main.rs, aşağıdakilerle çalıştırabilmeniz gerekir:

rustc src/lib.rs
rustc -L . main.rs
./main

Genel olarak, Rust kitabındaki "Crates and Modules" bölümü oldukça iyidir. Çok sayıda örnek var.

Son olarak, Rust derleyicisi sizin için otomatik olarak alt dizinlere de bakar. Örneğin, yukarıdaki kod bu dizin yapısıyla değişmeden çalışacaktır:

src/
  lib.rs
  vector/
      mod.rs
      vector_b.rs
main.rs

Derleme ve çalıştırma komutları da aynı kalır.


"Vektör" ile ne demek istediğimi yanlış anladığına inanıyorum. Veri yapısında değil, matematiksel nicelikte olduğu gibi vektörden bahsediyordum. Ayrıca, pasın en son sürümünü çalıştırmıyorum çünkü pencereler üzerine inşa etmek biraz acı verici.
starscape

+1 Tam olarak ihtiyacım olan şey değildi, ama beni doğru yönü gösterdi.
starscape

@EpicPineapple Gerçekten! Ve bu tür vektörleri temsil etmek için bir Vec kullanılabilir. (Elbette daha büyük N için.)
BurntSushi5

1
@EpicPineapple Güncelleyebilmem için cevabımın neyi kaçırdığını açıklayabilir misiniz? Senin cevabın ve benimki arasındaki farkı math::Vec2yerine kullanmak yerine görmeye çalışıyorum math::vector::Vec2. (yani, Aynı konsept ancak bir modül daha derin.)
BurntSushi5

1
Sorunuzda bu kriteri göremiyorum. Görebildiğim kadarıyla sorulan soruyu cevapladım. (Bu gerçekten modülleri dosyalardan nasıl ayıracağımı soruyordu.) Üzgünüm, Rust 0.9'da çalışmıyor, ama bu kararsız bir dil kullanma alanıyla birlikte geliyor.
BurntSushi5

45

Rust modülü kuralları:

  1. Bir kaynak dosya sadece kendi modülüdür (main.rs, lib.rs ve mod.rs özel dosyaları hariç).
  2. Bir dizin sadece bir modül yolu bileşenidir.
  3. Mod.rs dosyası sadece dizinin modülüdür.

Math dizinindeki matrix.rs 1 dosyası sadece modüldür math::matrix. Bu kolay. Dosya sisteminizde gördüklerinizi kaynak kodunuzda da bulabilirsiniz. Bu, dosya yollarının ve modül yollarının bire bir yazışmasıdır 2 .

Yani bir yapı alabilirsiniz Matrixile use math::matrix::Matrixyapı bir dizin matematik dosya matrix.rs içeride olduğundan,. Mutlu değil? use math::Matrix;Onun yerine çok tercih edersin , değil mi? Mümkün. Tanımlayıcıyı math::matrix::Matrixmatematik / mod.rs biçiminde yeniden dışa aktarın :

pub use self::math::Matrix;

Bunu çalıştırmak için başka bir adım daha var. Rust , modülü yüklemek için bir modül bildirimine ihtiyaç duyar . mod math;Main.rs.'de bir ekleyin Bunu yapmazsanız, şu şekilde içe aktarırken derleyiciden bir hata mesajı alırsınız:

error: unresolved import `math::Matrix`. Maybe a missing `extern crate math`?

Burada ipucu yanıltıcıdır. Ek sandıklara gerek yok, tabii ki gerçekten ayrı bir kitaplık yazmak niyetindesiniz.

Bunu main.rs'nin en üstüne ekleyin:

mod math;
pub use math::Matrix;

Modül Bildirgede ayrıca submodüller gerekli olduğu vector, matrixve complexçünkü, mathihtiyaçlar yeniden ihraç onları onları yüklemek için. Bir tanımlayıcının yeniden dışa aktarımı, yalnızca tanımlayıcının modülünü yüklediyseniz çalışır. Bu, math::matrix::Matrixyazmanız gereken tanımlayıcıyı yeniden dışa aktarmak anlamına gelir mod matrix;. Bunu matematik / mod.rs'da yapabilirsiniz. Bu nedenle, şu içeriğe sahip dosyayı oluşturun:

mod vector;
pub use self::vector::Vector;

mod matrix;
pub use self::matrix::Matrix;

mod complex;
pub use self::complex::Complex;

Aaave bitirdin.


1 Kaynak dosya adları genellikle Rust'ta küçük harfle başlar. Bu yüzden Matrix.rs değil matrix.rs kullanıyorum.

2 Java farklı. Yolu da ile ilan ediyorsunuz package. Gereksiz. Yol, dosya sistemindeki kaynak dosya konumundan zaten bellidir. Bu bilgiyi neden dosyanın en üstündeki bir beyanda tekrar edelim? Elbette bazen dosyanın dosya sistemi konumunu bulmak yerine kaynak koduna hızlı bir şekilde bakmak daha kolaydır. Daha az kafa karıştırıcı olduğunu söyleyen insanları anlayabiliyorum.


25

Rusts arayıcıları muhtemelen bana kafir diyecek ve bu çözümden nefret edeceklerdir, ancak bu çok daha basit: her şeyi kendi dosyasında yapın, ardından mod.rs'daki " dahil et! " Makrosunu kullanın:

include!("math/Matrix.rs");
include!("math/Vector.rs");
include!("math/Complex.rs");

Bu şekilde, eklenmiş iç içe modüller almazsınız ve karmaşık dışa aktarma ve yeniden yazma kurallarından kurtulursunuz. Basit, etkili, zahmetsiz.


1
Az önce isim aralığını attın. Bir dosyayı diğeriyle ilgisiz bir şekilde değiştirmek artık diğer dosyaları bozabilir. 'Kullanım' kullanımınız sızıntılı hale gelir (yani her şey gibidir use super::*). Kodu diğer dosyalardan gizleyemezsiniz (bu, güvenli olmayan güvenli soyutlamalar için önemlidir)
Demur Rumed

13
Evet, ama bu durumda tam olarak istediğim şey buydu: ad alanı için tek bir dosya gibi davranan birkaç dosyaya sahip olmak. Bunu her durum için savunmuyorum, ancak herhangi bir nedenle "dosya başına bir modül" yöntemiyle uğraşmak istemiyorsanız bu yararlı bir geçici çözümdür.
hasvn

Bu harika, modülümün yalnızca dahili ama kendi kendine yeten bir parçasına sahibim ve bu hile yaptı. Uygun modül çözümünü de çalıştırmaya çalışacağım, ancak bu hiç de kolay değil.
rjh

7
Kafir olarak adlandırılmak umurumda değil, çözümün uygun!
sailfish009

21

Pekala, derleyicimle bir süre savaştım ve sonunda onu çalıştırdım (BurntSushi'ye işaret ettiği için teşekkürler pub use.

main.rs:

use math::Vec2;
mod math;

fn main() {
  let a = Vec2{x: 10.0, y: 10.0};
  let b = Vec2{x: 20.0, y: 20.0};
}

matematik / mod.rs:

pub use self::vector::Vec2;
mod vector;

math / vector.rs

use std::num::sqrt;

pub struct Vec2 {
  x: f64,
  y: f64
}

impl Vec2 {
  pub fn len(&self) -> f64 {
    sqrt(self.x * self.x + self.y * self.y) 
  }

  // other methods...
}

Diğer yapılar da aynı şekilde eklenebilir. NOT: 0.9 ile derlenmiştir, usta değil.


4
Not o kullanımınız mod math;içinde main.rsçiftler için mainkitaplığınızla programı. mathModülünüzün bağımsız olmasını istiyorsanız, onu ayrı olarak derlemeniz ve ona bağlanmanız gerekir extern crate math(cevabımda gösterildiği gibi). Rust 0.9'da, sözdiziminin bunun extern mod mathyerine olması mümkündür .
BurntSushi5

20
BurntSushi5'in cevabını doğru olarak işaretlemek gerçekten adil olurdu.
IluTov

2
@NSAddict Hayır. Modülleri dosyalardan ayırmak için ayrı bir kasa oluşturmanıza gerek yoktur. Aşırı tasarlanmış.
nalply

1
Neden bu en çok oylanan cevap değil? Soru, projeyi birkaç dosyaya nasıl böleceğini sordu, bu cevabın gösterdiği kadar basit, kasalara nasıl bölüneceği değil, bu daha zor ve @ BurntSushi5'in cevapladığı şeydi (belki soru düzenlendi?). ..
Renato

6
@ BurntSushi5 'in cevabı kabul edilen cevap olmalıydı. Sosyal açıdan garip ve hatta belki bir soru sormak, çok güzel bir cevap almak, sonra bunu ayrı bir cevap olarak özetlemek ve özetinizi kabul edilen cevap olarak işaretlemek anlamına gelebilir.
hasanyasin

5

Buraya, iç içe geçmişken Rust dosyalarını nasıl dahil edeceğinizi eklemek istiyorum. Aşağıdaki yapıya sahibim:

|-----main.rs
|-----home/
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

Nasıl erişirsiniz sink.rsveya toilet.rsburadan main.rs?

Diğerlerinin de belirttiği gibi Rust'un dosyalar hakkında bilgisi yok. Bunun yerine, her şeyi modüller ve alt modüller olarak görür. Banyo dizininin içindeki dosyalara erişmek için bunları dışa aktarmanız veya en üste yerleştirmeniz gerekir. Bunu, erişmek istediğiniz dizine ve pub mod filename_inside_the_dir_without_rs_extdosyanın içine bir dosya adı belirterek yaparsınız .

Misal.

// sink.rs
pub fn run() { 
    println!("Wash my hands for 20 secs!");
}

// toilet.rs
pub fn run() {
    println!("Ahhh... This is sooo relaxing.")
}
  1. Dizinin bathroom.rsiçinde adlı bir dosya oluşturun home:

  2. Dosya adlarını dışa aktarın:

    // bathroom.rs
    pub mod sink;
    pub mod toilet;
    
  3. home.rsYanında adlı bir dosya oluşturunmain.rs

  4. pub mod bathroom.rs dosyası

    // home.rs
    pub mod bathroom;
    
  5. İçinde main.rs

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    mod home;
    
    fn main() {
        home::bathroom::sink::run();
    }
    

    use ifadeler de kullanılabilir:

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    use home::bathroom::{sink, toilet};
    
    fn main() {
        sink::run();
        sink::toilet();
    }
    

Alt modüller içinde diğer kardeş modülleri (dosyaları) dahil etme

Eğer kullanmak istediğiniz sink.rsdan toilet.rssen belirterek modülü çağırabilir, selfya superanahtar kelimeleri.

// inside toilet.rs
use self::sink;
pub fn run() {
  sink::run();
  println!("Ahhh... This is sooo relaxing.")
}

Nihai Dizin Yapısı

Bunun gibi bir şeye sahip olacaksın:

|-----main.rs
|-----home.rs
|-----home/
|---------bathroom.rs
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

Yukarıdaki yapı yalnızca Rust 2018'den itibaren çalışır. Aşağıdaki dizin yapısı 2018 için de geçerlidir, ancak 2015'in çalışma şekli böyledir.

|-----main.rs
|-----home/
|---------mod.rs
|---------bathroom/
|-----------------mod.rs
|-----------------sink.rs
|-----------------toilet.rs

İçinde home/mod.rsaynı olan ./home.rsve home/bathroom/mod.rsaynı olan home/bathroom.rs. Rust bu değişikliği, dizinle aynı ada sahip bir dosya eklerseniz derleyicinin kafası karışacağı için yaptı. 2018 sürümü (ilk gösterilen) bu yapıyı düzeltir.

Daha fazla bilgi için bu depoya ve genel bir açıklama için bu YouTube videosuna bakın.

Son bir şey ... tirelerden kaçının! snake_caseBunun yerine kullanın .

Önemli Not

Sen gerekir derin dosyalar üst düzey olanlar tarafından gerekli olmasalar bile, üstüne varil tüm dosyaları.

Bu, sink.rskeşfetmek için toilet.rs, yukarıda belirtilen yöntemleri kullanarak onları namlu haline getirmeniz gerektiği anlamına gelir main.rs!

Bir başka deyişle, yaparken pub mod sink;veya use self::sink;toilet.rsolacak iş değil sen onlara tüm yol maruz sürece main.rs!

Bu nedenle, dosyalarınızı her zaman en üste koymayı unutmayın!


3
... C ++ ile karşılaştırıldığında delice kıvrımlı, bu da bir şey söylüyor
Joseph Garvin
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.