कई फ़ाइलों में एक मॉड्यूल विभाजित करें


103

मैं इसमें एक से अधिक स्ट्रक्चर वाला एक मॉड्यूल रखना चाहता हूं, प्रत्येक इसकी अपनी फाइल में। Mathएक उदाहरण के रूप में एक मॉड्यूल का उपयोग करना :

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

मैं चाहता हूं कि प्रत्येक संरचना एक ही मॉड्यूल में हो, जिसे मैं अपनी मुख्य फ़ाइल से उपयोग करूंगा, जैसे:

use Math::Vector;

fn main() {
  // ...
}

हालांकि, रस्ट का मॉड्यूल सिस्टम (जो शुरू करने के लिए थोड़ा भ्रमित है) ऐसा करने का एक स्पष्ट तरीका प्रदान नहीं करता है। यह केवल आपको एक फ़ाइल में अपना पूरा मॉड्यूल रखने की अनुमति देता है। क्या यह अन-देहाती है? यदि नहीं, तो मैं यह कैसे करूँ?


1
मैंने व्याख्या की "मैं इसमें कई सारे पुर्जों के साथ एक मॉड्यूल रखना चाहता हूं, प्रत्येक में यह अपनी फाइल है।" इसका मतलब यह है कि आप अपनी फ़ाइल में प्रत्येक संरचना की परिभाषा चाहते थे।
BurntSushi5

1
इसे देहाती नहीं माना जाएगा, हालांकि मॉड्यूल सिस्टम निश्चित रूप से ऐसी संरचना की अनुमति देता है। यह आम तौर पर एक मॉड्यूल पथ के लिए बेहतर करने के लिए सीधे एक फ़ाइल सिस्टम पथ, जैसे struct के अनुरूप है foo::bar::Bazमें परिभाषित किया जाना चाहिए foo/bar.rsया foo/bar/mod.rs
क्रिस मॉर्गन

जवाबों:


111

रस्ट का मॉड्यूल सिस्टम वास्तव में अविश्वसनीय रूप से लचीला है और आपको यह जानने की अनुमति देगा कि आप अपने कोड को फ़ाइलों में किस तरह से संरचित करते हैं।

मुझे लगता है कि यहां कुंजी का उपयोग करना है pub use, जो आपको अन्य मॉड्यूल से पहचानकर्ताओं को फिर से निर्यात करने की अनुमति देगा। रस्ट के std::ioटोकरे में इसके लिए मिसाल है, जहां उप-मॉड्यूल से कुछ प्रकार फिर से उपयोग के लिए निर्यात किएstd::io जाते हैं ।

संपादित करें (2019-08-25): उत्तर के निम्नलिखित भाग को कुछ समय पहले लिखा गया था। यह बताता है कि इस तरह के एक मॉड्यूल संरचना को rustcअकेले कैसे सेटअप किया जाए। आज, एक आमतौर पर अधिकांश उपयोग के मामलों के लिए कार्गो का उपयोग करेगा। हालांकि निम्नलिखित अभी भी मान्य है, इसके कुछ हिस्से (जैसे #![crate_type = ...]) अजीब लग सकते हैं। यह अनुशंसित समाधान नहीं है।

अपने उदाहरण को अनुकूलित करने के लिए, हम इस निर्देशिका संरचना के साथ शुरुआत कर सकते हैं:

src/
  lib.rs
  vector.rs
main.rs

यहाँ आपका है main.rs:

extern crate math;

use math::vector;

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

और आपका src/lib.rs:

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

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

और अंत में 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![] }
        }
    }
}

और यहीं से जादू होता है। हमने एक उप-मॉड्यूल को परिभाषित किया है math::vector::vector_aजिसमें एक विशेष प्रकार के वेक्टर के कुछ कार्यान्वयन हैं। लेकिन हम नहीं चाहते हैं कि आपकी लाइब्रेरी के ग्राहक इस बात का ध्यान रखें कि vector_aसब-मॉड्यूल हो। इसके बजाय, हम इसे math::vectorमॉड्यूल में उपलब्ध कराना चाहेंगे । इसके साथ किया जाता है pub use self::vector_a::VectorA, जो vector_a::VectorAवर्तमान मॉड्यूल में पहचानकर्ता को फिर से निर्यात करता है ।

लेकिन आपने पूछा कि यह कैसे करना है ताकि आप अपने विशेष वेक्टर कार्यान्वयन को विभिन्न फाइलों में डाल सकें। यह वही है जो mod vector_b;लाइन करता है। यह रस्ट कंपाइलर को vector_b.rsउस मॉड्यूल के कार्यान्वयन के लिए एक फ़ाइल देखने के लिए निर्देश देता है । और यकीन है कि यहाँ, हमारी src/vector_b.rsफ़ाइल है:

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

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

ग्राहक के दृष्टिकोण से, तथ्य यह है कि VectorAऔर VectorBदो अलग अलग फ़ाइलों में दो अलग-अलग मॉड्यूल में परिभाषित कर रहे हैं पूरी तरह से अपारदर्शी है।

यदि आप उसी निर्देशिका में हैं main.rs, तो आपको इसे चलाने में सक्षम होना चाहिए:

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

सामान्य तौर पर, रस्ट बुक में "क्रेट्स एंड मॉड्यूल्स" अध्याय बहुत अच्छा है। इसके बहुत सारे उदाहरण हैं।

अंत में, रस्ट कंपाइलर स्वचालित रूप से आपके लिए उप-निर्देशिकाओं में भी दिखता है। उदाहरण के लिए, उपरोक्त कोड इस निर्देशिका संरचना के साथ अपरिवर्तित काम करेगा:

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

संकलित करने और चलाने के आदेश समान रूप से बने रहते हैं।


मेरा मानना ​​है कि आपको "वेक्टर" से मेरा मतलब गलत समझा गया। मैं गणितीय मात्रा में वेक्टर की बात कर रहा था , न कि डेटा संरचना की। इसके अलावा, मैं जंग के सबसे अंतिम संस्करण को नहीं चला रहा हूं, क्योंकि यह विंडोज़ पर बनाने के लिए थोड़ा सा दर्द है।
स्टारस्केप

+1 ठीक वैसा नहीं था जैसा मुझे चाहिए था, लेकिन मुझे सही दिशा में इशारा किया।
स्टारस्केप

@EpicPineapple वास्तव में! और इस तरह के वैक्टर का प्रतिनिधित्व करने के लिए वीईसी का उपयोग किया जा सकता है। (बड़े एन के लिए, निश्चित रूप से।)
BurntSushi5

1
@EpicPineapple क्या आप बता सकते हैं कि मेरा उत्तर क्या छूट गया है ताकि मैं इसे अपडेट कर सकूं? मैं आपके जवाब और मेरे बीच के अंतर को देखने के math::Vec2बजाय संघर्ष कर रहा हूं math::vector::Vec2। (यानी, एक ही अवधारणा लेकिन एक मॉड्यूल गहरा।)
BurntSushi5

1
मुझे आपके प्रश्न में वह मापदंड दिखाई नहीं दे रहा है। जहां तक ​​मैं देख सकता हूं, मैंने पूछे गए सवाल का जवाब दिया है। (जो वास्तव में पूछ रहा था कि फाइलों से मॉड्यूल को कैसे तलाक दिया जाए।) इसके बारे में खेद है कि यह रस्ट 0.9 पर काम नहीं कर रहा है, लेकिन यह एक अस्थिर भाषा का उपयोग करने के क्षेत्र के साथ आता है।
BurntSushi5

40

जंग मॉड्यूल नियम हैं:

  1. एक स्रोत फ़ाइल सिर्फ अपना स्वयं का मॉड्यूल है (विशेष फ़ाइलों को छोड़कर। मुख्य, lib.rs और mod.rs)।
  2. एक निर्देशिका सिर्फ एक मॉड्यूल पथ घटक है।
  3. फ़ाइल mod.rs सिर्फ डायरेक्टरी का मॉड्यूल है।

डायरेक्टरी मैथ में फाइल मैट्रिक्स.र्स 1 सिर्फ मॉड्यूल है math::matrix। यह आसान है। आप अपने फाइल सिस्टम पर जो देखते हैं वह आपको अपने सोर्स कोड में भी मिल जाता है। यह फ़ाइल पथ और मॉड्यूल पथ 2 का एक-से-एक पत्राचार है ।

तो तुम एक struct आयात कर सकते हैं Matrixके साथ use math::matrix::Matrixहै, क्योंकि struct एक निर्देशिका गणित में फ़ाइल matrix.rs के अंदर है। खुश नहीं? आप use math::Matrix;इसके बजाय बहुत ज्यादा पसंद करेंगे , है ना? यह संभव है। पहचानकर्ता math::matrix::Matrixको गणित / mod.rs में फिर से निर्यात करें :

pub use self::math::Matrix;

यह काम पाने के लिए एक और कदम है। मॉड्यूल को लोड करने के लिए जंग को एक मॉड्यूल घोषणा की आवश्यकता होती है । mod math;Main.rs में जोड़ें यदि आप ऐसा नहीं करते हैं, तो आपको इस तरह का आयात करते समय कंपाइलर से एक त्रुटि संदेश मिलता है:

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

संकेत यहाँ भ्रामक है। अतिरिक्त टोकरे की कोई आवश्यकता नहीं है, बेशक आप वास्तव में एक अलग पुस्तकालय लिखने का इरादा रखते हैं।

इसे main.rs के शीर्ष पर जोड़ें:

mod math;
pub use math::Matrix;

मॉड्यूल घोषणा भी सबमोडुल्स के लिए आवश्यक है vector, matrixऔर complex, क्योंकि mathउन्हें फिर से निर्यात करने के लिए उन्हें लोड करने की आवश्यकता है। एक पहचानकर्ता का फिर से निर्यात केवल तभी काम करता है जब आपने पहचानकर्ता के मॉड्यूल को लोड किया हो। इसका मतलब है, उस पहचानकर्ता को फिर से निर्यात करना जो math::matrix::Matrixआपको लिखना है mod matrix;। आप इसे गणित / mod.rs में कर सकते हैं। इसलिए इस सामग्री के साथ फाइल बनाएं:

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

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

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

आन्दोलन आप कर रहे हैं।


1 स्रोत फ़ाइल नाम आमतौर पर जंग में एक लोअरकेस अक्षर से शुरू होता है। इसलिए मैं मैट्रिक्स का उपयोग करता हूं। न कि मैट्रिक्स का।

2 जावा का अलग। आप के साथ packageभी मार्ग घोषित करते हैं। यह बेमानी है। फ़ाइल सिस्टम में स्रोत फ़ाइल स्थान से पथ पहले से ही स्पष्ट है। फ़ाइल के शीर्ष पर एक घोषणा में इस जानकारी को क्यों दोहराएं? बेशक कभी-कभी फ़ाइल के फाइल सिस्टम स्थान का पता लगाने के बजाय स्रोत कोड पर त्वरित नज़र रखना आसान होता है। मैं ऐसे लोगों को समझ सकता हूं जो कहते हैं कि यह कम भ्रमित करने वाला है।


25

रस्ट्स शुद्धतावादी शायद मुझे एक विधर्मी कहेंगे और इस समाधान से नफरत करेंगे, लेकिन यह बहुत सरल है: बस प्रत्येक चीज़ को अपनी फ़ाइल में करें, फिर mod.rs में " शामिल! " मैक्रो का उपयोग करें:

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

इस तरह से आपको कोई जोड़ा हुआ नेस्टेड मॉड्यूल नहीं मिलता है, और जटिल निर्यात और नियमों को फिर से लिखने से बचें। सरल, प्रभावी, कोई उपद्रव नहीं।


1
आपने सिर्फ नाम-स्थान बाहर फेंक दिया। एक फ़ाइल को दूसरे से असंबंधित तरीके से बदलना अब अन्य फ़ाइलों को तोड़ सकता है। 'उपयोग' का आपका उपयोग टपका (यानी सब कुछ जैसा है use super::*) हो जाता है । आप अन्य फ़ाइलों से कोड छिपा नहीं सकते हैं (जो असुरक्षित उपयोग करने वाले सुरक्षित
गर्भपात के

13
हां, लेकिन यह वही है जो मैं उस मामले में चाहता था: कई फाइलें हैं जो नाम स्थान के प्रयोजनों के लिए सिर्फ एक के रूप में व्यवहार करती हैं। मैं हर मामले के लिए इसकी वकालत नहीं कर रहा हूं, लेकिन अगर आप किसी भी कारण से "एक मॉड्यूल प्रति फ़ाइल" विधि से निपटना नहीं चाहते हैं तो यह एक उपयोगी समाधान है।
हसन

यह बहुत अच्छा है, मेरे पास मेरे मॉड्यूल का एक हिस्सा है जो केवल आंतरिक है लेकिन आत्म-निहित है, और इसने यह चाल चली। मैं भी काम करने के लिए उचित मॉड्यूल समाधान प्राप्त करने की कोशिश करूंगा, लेकिन यह कहीं भी उतना आसान नहीं है।
rjh

7
मुझे विधर्मी कहे जाने की परवाह नहीं है, आपका समाधान सुविधाजनक है!
सेलफिश 009

21

ठीक है, मेरे कंपाइलर को थोड़ी देर के लिए लड़ा और आखिरकार इसे काम करने के लिए मिला (इशारा करने के लिए बर्नटूशी के लिए धन्यवाद 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};
}

गणित / mod.rs:

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

गणित / 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...
}

अन्य संरचनाओं को एक ही तरीके से जोड़ा जा सकता है। नोट: 0.9 के साथ संकलित, मास्टर नहीं।


4
ध्यान दें कि जोड़ों mod math;में आपका उपयोग आपके पुस्तकालय के साथ आपके कार्यक्रम। यदि आप चाहते हैं कि आपका मॉड्यूल स्वतंत्र हो, तो आपको इसे अलग से संकलित करना होगा और इसके साथ लिंक करना होगा (जैसा कि मेरे उत्तर में दिखाया गया है)। रस्ट 0.9 में, यह संभव है कि सिंटैक्स इसके बजाय है। main.rsmainmathextern crate mathextern mod math
BurntSushi5

20
यह वास्तव में BurntSushi5 के उत्तर को सही के रूप में चिह्नित करने के लिए उचित होगा।
17

2
@NSAddict नहीं। फ़ाइलों से तलाक के मॉड्यूल के लिए आपको एक अलग टोकरा बनाने की आवश्यकता नहीं है। यह अधिक इंजीनियर है।
15:53

1
यह शीर्ष मत का जवाब क्यों नहीं है ?? प्रश्न पूछा गया कि प्रोजेक्ट को कुछ फ़ाइलों में कैसे विभाजित किया जाए, जो कि यह उत्तर जितना सरल है, उतना नहीं कि इसे क्रेट में कैसे विभाजित किया जाए, जो कठिन है और जो @ BurntSushi5 ने उत्तर दिया (शायद प्रश्न संपादित किया गया था?)। ..
रेनाटो

6
@ BurntSushi5 का उत्तर स्वीकृत उत्तर होना चाहिए था। यह सामाजिक रूप से अजीब है और शायद एक प्रश्न पूछने का भी मतलब है, एक बहुत अच्छा उत्तर प्राप्त करें, फिर इसे एक अलग उत्तर के रूप में संक्षेप में प्रस्तुत करें और स्वीकृत उत्तर के रूप में अपने सारांश को चिह्नित करें।
हैनानसीन

5

मैं यहाँ जोड़ना चाहता हूँ कि जब आप गहराई से नेस्टेड होते हैं तो आप रस्ट फ़ाइलों को कैसे शामिल करते हैं। मेरे पास निम्नलिखित संरचना है:

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

आप कैसे sink.rsया toilet.rsसे पहुँचते हैं main.rs?

जैसा कि दूसरों ने उल्लेख किया है, रस्ट को फाइलों का कोई ज्ञान नहीं है। इसके बजाय यह सब कुछ मॉड्यूल और सबमॉड्यूल के रूप में देखता है। बाथरूम निर्देशिका के अंदर फ़ाइलों तक पहुंचने के लिए आपको उन्हें निर्यात करने या उन्हें शीर्ष पर बैरल करने की आवश्यकता है। आप निर्देशिका के साथ फ़ाइल नाम निर्दिष्ट करके ऐसा करना चाहते हैं जिसे आप pub mod filename_inside_the_dir_without_rs_extफ़ाइल तक पहुंचना चाहते हैं ।

उदाहरण।

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

// toilet.rs
pub fn run() {
    println!("Ahhh... This is sooo relaxing.")
}
  1. निर्देशिका के bathroom.rsअंदर नामक एक फ़ाइल बनाएँ home:

  2. फ़ाइल नाम निर्यात करें:

    // bathroom.rs
    pub mod sink;
    pub mod toilet;
  3. home.rsबगल में एक फ़ाइल बनाएँmain.rs

  4. pub mod बाथरूम .rs फ़ाइल

    // home.rs
    pub mod bathroom;
  5. अंदर 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 बयान भी इस्तेमाल किया जा सकता है:

    // 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();
    }

सबमोडुल्स के भीतर अन्य भाई मॉड्यूल (फाइलें) सहित

मामले में आप उपयोग करना चाहते हैं sink.rsसे toilet.rs, आप निर्दिष्ट करके मॉड्यूल कॉल कर सकते हैं selfया superकीवर्ड।

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

अंतिम निर्देशिका संरचना

आप कुछ इस तरह से समाप्त करेंगे:

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

ऊपर की संरचना केवल रुस्ट 2018 के साथ काम करती है। निम्न निर्देशिका संरचना 2018 के लिए भी मान्य है, लेकिन यह है कि 2015 कैसे काम करता था।

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

जिसमें home/mod.rsजैसा है ./home.rsऔर home/bathroom/mod.rsजैसा है, वैसा ही है home/bathroom.rs। जंग ने यह परिवर्तन किया क्योंकि संकलक भ्रमित हो जाएगा यदि आपने निर्देशिका के समान नाम के साथ एक फ़ाइल शामिल की। 2018 संस्करण (पहले दिखाया गया है) उस संरचना को ठीक करता है।

देखें इस रेपो में अधिक जानकारी है और इस के लिए यूट्यूब वीडियो एक समग्र विवरण के लिए।

एक आखिरी बात ... हाइफ़न से बचें! snake_caseइसके बजाय उपयोग करें ।

महत्वपूर्ण लेख

आपको सभी फ़ाइलों को शीर्ष पर बैरल करना चाहिए , भले ही गहरी फ़ाइलों को शीर्ष-स्तरीय लोगों द्वारा आवश्यक न हो।

इसका मतलब है, कि sink.rsखोज के लिए toilet.rs, आपको सभी तरीकों से ऊपर के तरीकों का उपयोग करके उन्हें बैरल करना होगा main.rs!

दूसरे शब्दों में, जब तक आपने उन्हें पूरी तरह से उजागर नहीं कर दिया, तब तक अंदर pub mod sink;या use self::sink;अंदर काम नहींtoilet.rs करेगा !main.rs

इसलिए, हमेशा अपनी फ़ाइलों को शीर्ष पर रखने के लिए याद रखें!


2
... जो C ++ की तुलना में पागलपन का दोषी है, जो कुछ कह रहा है
जोसेफ गार्विन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.