Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

use কীওয়ার্ড ব্যবহার করে স্কোপে পাথ নিয়ে আসা

ফাংশন কল করার জন্য বারবার পুরো পাথ লেখাটা বেশ অসুবিধাজনক এবং পুনরাবৃত্তিমূলক মনে হতে পারে। Listing 7-7-এ, আমরা add_to_waitlist ফাংশনের জন্য অ্যাবসোলিউট বা রিলেটিভ পাথ যাই বেছে নিই না কেন, প্রতিবার add_to_waitlist কল করার সময় আমাদের front_of_house এবং hosting-ও নির্দিষ্ট করতে হয়েছিল। সৌভাগ্যবশত, এই প্রক্রিয়াটি সহজ করার একটি উপায় আছে: আমরা use কীওয়ার্ড দিয়ে একবার একটি পাথের শর্টকাট তৈরি করে নিতে পারি এবং তারপর স্কোপের অন্য সব জায়গায় ছোট নামটি ব্যবহার করতে পারি।

Listing 7-11-এ, আমরা crate::front_of_house::hosting মডিউলটিকে eat_at_restaurant ফাংশনের স্কোপে নিয়ে এসেছি, তাই eat_at_restaurant-এর মধ্যে add_to_waitlist ফাংশনটি কল করার জন্য আমাদের কেবল hosting::add_to_waitlist নির্দিষ্ট করতে হবে।

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

একটি স্কোপে use এবং একটি পাথ যোগ করা ফাইলসিস্টেমে একটি সিম্বলিক লিঙ্ক তৈরি করার মতো। ক্রেট রুটে use crate::front_of_house::hosting যোগ করার মাধ্যমে, hosting এখন সেই স্কোপে একটি বৈধ নাম, ঠিক যেন hosting মডিউলটি ক্রেট রুটেই ডিফাইন করা হয়েছিল। use দিয়ে স্কোপে আনা পাথগুলোও অন্য যেকোনো পাথের মতোই প্রাইভেসি পরীক্ষা করে।

মনে রাখবেন যে use শুধুমাত্র সেই নির্দিষ্ট স্কোপের জন্য শর্টকাট তৈরি করে যেখানে use ব্যবহার করা হয়েছে। Listing 7-12-এ eat_at_restaurant ফাংশনটিকে customer নামের একটি নতুন চাইল্ড মডিউলে সরানো হয়েছে, যা use স্টেটমেন্টের থেকে ভিন্ন একটি স্কোপ, তাই ফাংশনের বডি কম্পাইল হবে না।

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

কম্পাইলার এরর দেখায় যে শর্টকাটটি আর customer মডিউলের মধ্যে প্রযোজ্য নয়:

$ cargo build
   Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0433]: failed to resolve: use of undeclared crate or module `hosting`
  --> src/lib.rs:11:9
   |
11 |         hosting::add_to_waitlist();
   |         ^^^^^^^ use of undeclared crate or module `hosting`
   |
help: consider importing this module through its public re-export
   |
10 +     use crate::hosting;
   |

warning: unused import: `crate::front_of_house::hosting`
 --> src/lib.rs:7:5
  |
7 | use crate::front_of_house::hosting;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

For more information about this error, try `rustc --explain E0433`.
warning: `restaurant` (lib) generated 1 warning
error: could not compile `restaurant` (lib) due to 1 previous error; 1 warning emitted

লক্ষ্য করুন, একটি ওয়ার্নিংও রয়েছে যে use তার স্কোপে আর ব্যবহৃত হচ্ছে না! এই সমস্যাটি সমাধান করার জন্য, use-কে customer মডিউলের ভিতরেও সরিয়ে নিন, অথবা চাইল্ড customer মডিউলের মধ্যে প্যারেন্ট মডিউলের শর্টকাটটিকে super::hosting দিয়ে রেফারেন্স করুন।

প্রচলিত use পাথ তৈরি করা

Listing 7-11-এ, আপনি হয়তো ভেবেছিলেন কেন আমরা use crate::front_of_house::hosting নির্দিষ্ট করেছি এবং তারপর eat_at_restaurant-এ hosting::add_to_waitlist কল করেছি, যেখানে আমরা use পাথটিকে add_to_waitlist ফাংশন পর্যন্ত প্রসারিত করে একই ফলাফল অর্জন করতে পারতাম, যেমনটি Listing 7-13-এ দেখানো হয়েছে।

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting::add_to_waitlist;

pub fn eat_at_restaurant() {
    add_to_waitlist();
}

যদিও Listing 7-11 এবং Listing 7-13 উভয়ই একই কাজ সম্পন্ন করে, use দিয়ে একটি ফাংশনকে স্কোপে আনার প্রচলিত (idiomatic) উপায় হলো Listing 7-11। use দিয়ে ফাংশনের প্যারেন্ট মডিউলকে স্কোপে আনার মানে হলো ফাংশনটি কল করার সময় আমাদের প্যারেন্ট মডিউলটি নির্দিষ্ট করতে হবে। ফাংশন কল করার সময় প্যারেন্ট মডিউল নির্দিষ্ট করা এটা স্পষ্ট করে যে ফাংশনটি স্থানীয়ভাবে ডিফাইন করা হয়নি, এবং একই সাথে সম্পূর্ণ পাথের পুনরাবৃত্তিও কমায়। Listing 7-13-এর কোডটি অস্পষ্ট কারণ add_to_waitlist কোথায় ডিফাইন করা হয়েছে তা বোঝা যায় না।

অন্যদিকে, use দিয়ে struct, enum এবং অন্যান্য আইটেম আনার সময়, সম্পূর্ণ পাথ নির্দিষ্ট করাই প্রচলিত রীতি। Listing 7-14 স্ট্যান্ডার্ড লাইব্রেরির HashMap struct-কে একটি বাইনারি ক্রেটের স্কোপে আনার প্রচলিত উপায় দেখায়।

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

এই রীতির পিছনে কোনো শক্তিশালী কারণ নেই: এটি কেবল একটি কনভেনশন যা সময়ের সাথে তৈরি হয়েছে, এবং লোকেরা এইভাবেই রাস্ট কোড পড়তে এবং লিখতে অভ্যস্ত হয়ে গেছে।

এই রীতির ব্যতিক্রম হলো যদি আমরা use স্টেটমেন্ট ব্যবহার করে একই নামের দুটি আইটেমকে স্কোপে নিয়ে আসি, কারণ রাস্ট এটির অনুমতি দেয় না। Listing 7-15 দেখায় কিভাবে একই নামের কিন্তু ভিন্ন প্যারেন্ট মডিউলের দুটি Result টাইপকে স্কোপে আনা যায় এবং কীভাবে তাদের রেফার করতে হয়।

use std::fmt;
use std::io;

fn function1() -> fmt::Result {
    // --snip--
    Ok(())
}

fn function2() -> io::Result<()> {
    // --snip--
    Ok(())
}

আপনি দেখতে পাচ্ছেন, প্যারেন্ট মডিউল ব্যবহার করে দুটি Result টাইপকে আলাদা করা হয়েছে। যদি আমরা use std::fmt::Result এবং use std::io::Result নির্দিষ্ট করতাম, তাহলে আমাদের একই স্কোপে দুটি Result টাইপ থাকত, এবং যখন আমরা Result ব্যবহার করতাম, রাস্ট জানত না আমরা কোনটি বোঝাতে চাইছি।

as কীওয়ার্ড দিয়ে নতুন নাম প্রদান করা

use দিয়ে একই নামের দুটি টাইপকে একই স্কোপে আনার সমস্যার আরেকটি সমাধান আছে: পাথের পরে, আমরা as এবং টাইপের জন্য একটি নতুন স্থানীয় নাম বা alias নির্দিষ্ট করতে পারি। Listing 7-16-এ, Listing 7-15-এর কোডটি লেখার আরেকটি উপায় দেখানো হয়েছে, যেখানে as ব্যবহার করে দুটি Result টাইপের মধ্যে একটির নাম পরিবর্তন করা হয়েছে।

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    // --snip--
    Ok(())
}

fn function2() -> IoResult<()> {
    // --snip--
    Ok(())
}

দ্বিতীয় use স্টেটমেন্টে, আমরা std::io::Result টাইপের জন্য নতুন নাম IoResult বেছে নিয়েছি, যা std::fmt থেকে আনা Result-এর সাথে冲突 করবে না, যেটিও আমরা স্কোপে নিয়ে এসেছি। Listing 7-15 এবং Listing 7-16 উভয়ই প্রচলিত হিসাবে বিবেচিত হয়, তাই পছন্দ আপনার!

pub use দিয়ে নাম পুনরায়-এক্সপোর্ট করা

যখন আমরা use কীওয়ার্ড দিয়ে একটি নামকে স্কোপে নিয়ে আসি, তখন নামটি সেই স্কোপের জন্য প্রাইভেট থাকে যেখানে আমরা এটি ইম্পোর্ট করেছি। সেই স্কোপের বাইরের কোডকে সেই নামটি এমনভাবে রেফার করতে সক্ষম করার জন্য যেন এটি সেই স্কোপেই ডিফাইন করা হয়েছিল, আমরা pub এবং use একত্রিত করতে পারি। এই কৌশলটিকে রি-এক্সপোর্টিং (re-exporting) বলা হয় কারণ আমরা একটি আইটেমকে স্কোপে নিয়ে আসছি এবং একই সাথে সেই আইটেমটিকে অন্যদের তাদের স্কোপে আনার জন্য উপলব্ধ করে দিচ্ছি।

Listing 7-17-এ Listing 7-11-এর কোডটি দেখানো হয়েছে যেখানে রুট মডিউলের use-কে pub use-এ পরিবর্তন করা হয়েছে।

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

এই পরিবর্তনের আগে, এক্সটার্নাল কোডকে restaurant::front_of_house::hosting::add_to_waitlist() পাথ ব্যবহার করে add_to_waitlist ফাংশনটি কল করতে হতো, যার জন্য front_of_house মডিউলটিকেও pub হিসাবে চিহ্নিত করার প্রয়োজন হতো। এখন যেহেতু এই pub use রুট মডিউল থেকে hosting মডিউলটিকে রি-এক্সপোর্ট করেছে, এক্সটার্নাল কোড এর পরিবর্তে restaurant::hosting::add_to_waitlist() পাথ ব্যবহার করতে পারে।

রি-এক্সপোর্টিং তখন উপযোগী যখন আপনার কোডের অভ্যন্তরীণ কাঠামো এবং আপনার কোড কল করা প্রোগ্রামাররা ডোমেইন সম্পর্কে যেভাবে চিন্তা করেন, তা ভিন্ন হয়। উদাহরণস্বরূপ, এই রেস্তোরাঁর রূপকে, রেস্তোরাঁ পরিচালনাকারী ব্যক্তিরা “ফ্রন্ট অফ হাউস” এবং “ব্যাক অফ হাউস” নিয়ে ভাবেন। কিন্তু রেস্তোরাঁয় আসা গ্রাহকরা সম্ভবত রেস্তোরাঁর অংশগুলো সম্পর্কে সেই পরিভাষায় ভাববেন না। pub use-এর মাধ্যমে, আমরা আমাদের কোড একটি কাঠামোতে লিখতে পারি কিন্তু একটি ভিন্ন কাঠামো এক্সপোজ করতে পারি। এটি করলে আমাদের লাইব্রেরিটি লাইব্রেরিতে কাজ করা প্রোগ্রামারদের জন্য এবং লাইব্রেরি কল করা প্রোগ্রামারদের জন্য সুসংগঠিত হয়। আমরা pub use-এর আরেকটি উদাহরণ এবং এটি কীভাবে আপনার ক্রেটের ডকুমেন্টেশনকে প্রভাবিত করে তা অধ্যায় ১৪-এর pub use দিয়ে একটি সুবিধাজনক পাবলিক API এক্সপোর্ট করা” বিভাগে দেখব।

এক্সটার্নাল প্যাকেজ ব্যবহার করা

দ্বিতীয় অধ্যায়ে, আমরা একটি গেসিং গেম প্রজেক্ট প্রোগ্রাম করেছিলাম যা র‍্যান্ডম নম্বর পেতে rand নামের একটি এক্সটার্নাল প্যাকেজ ব্যবহার করেছিল। আমাদের প্রজেক্টে rand ব্যবহার করার জন্য, আমরা Cargo.toml-এ এই লাইনটি যোগ করেছিলাম:

rand = "0.8.5"

Cargo.toml-এ rand-কে একটি ডিপেন্ডেন্সি হিসাবে যোগ করা কার্গোকে crates.io থেকে rand প্যাকেজ এবং এর যেকোনো ডিপেন্ডেন্সি ডাউনলোড করতে এবং rand আমাদের প্রজেক্টের জন্য উপলব্ধ করতে বলে।

তারপর, rand ডেফিনিশনগুলোকে আমাদের প্যাকেজের স্কোপে আনতে, আমরা ক্রেটের নাম, rand দিয়ে শুরু হওয়া একটি use লাইন যোগ করেছিলাম এবং যে আইটেমগুলো আমরা স্কোপে আনতে চেয়েছিলাম তা তালিকাভুক্ত করেছিলাম। মনে করুন যে দ্বিতীয় অধ্যায়ের “একটি র‍্যান্ডম নম্বর জেনারেট করা” অংশে, আমরা Rng ট্রেইটটিকে স্কোপে নিয়ে এসেছিলাম এবং rand::thread_rng ফাংশনটি কল করেছিলাম:

use std::io;

use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {guess}");
}

রাস্ট কমিউনিটির সদস্যরা crates.io-তে অনেক প্যাকেজ উপলব্ধ করেছেন, এবং সেগুলোর যেকোনো একটিকে আপনার প্যাকেজে অন্তর্ভুক্ত করার জন্য এই একই পদক্ষেপগুলো অনুসরণ করতে হয়: সেগুলোকে আপনার প্যাকেজের Cargo.toml ফাইলে তালিকাভুক্ত করা এবং use ব্যবহার করে তাদের ক্রেট থেকে আইটেমগুলোকে স্কোপে আনা।

মনে রাখবেন যে স্ট্যান্ডার্ড std লাইব্রেরিও একটি ক্রেট যা আমাদের প্যাকেজের জন্য এক্সটার্নাল। যেহেতু স্ট্যান্ডার্ড লাইব্রেরিটি রাস্ট ভাষার সাথে সরবরাহ করা হয়, তাই std-কে অন্তর্ভুক্ত করার জন্য আমাদের Cargo.toml পরিবর্তন করার প্রয়োজন নেই। কিন্তু সেখান থেকে আইটেমগুলোকে আমাদের প্যাকেজের স্কোপে আনতে আমাদের use দিয়ে এটিকে রেফার করতে হবে। উদাহরণস্বরূপ, HashMap-এর জন্য আমরা এই লাইনটি ব্যবহার করব:

#![allow(unused)]
fn main() {
use std::collections::HashMap;
}

এটি একটি অ্যাবসোলিউট পাথ যা std দিয়ে শুরু হয়, যা স্ট্যান্ডার্ড লাইব্রেরি ক্রেটের নাম।

বড় use তালিকা পরিষ্কার করতে নেস্টেড পাথ ব্যবহার করা

যদি আমরা একই ক্রেট বা একই মডিউলে ডিফাইন করা একাধিক আইটেম ব্যবহার করি, তবে প্রতিটি আইটেমকে তার নিজস্ব লাইনে তালিকাভুক্ত করলে আমাদের ফাইলগুলিতে অনেক উল্লম্ব স্থান (vertical space) নষ্ট হতে পারে। উদাহরণস্বরূপ, Listing 2-4-এর গেসিং গেমে আমাদের এই দুটি use স্টেটমেন্ট std থেকে আইটেমগুলোকে স্কোপে এনেছিল:

use rand::Rng;
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {guess}");

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

এর পরিবর্তে, আমরা একই আইটেমগুলোকে এক লাইনে স্কোপে আনতে নেস্টেড পাথ ব্যবহার করতে পারি। আমরা এটি করি পাথের সাধারণ অংশ নির্দিষ্ট করে, তারপরে দুটি কোলন, এবং তারপর কোঁকড়া বন্ধনীর (curly brackets) মধ্যে পাথের বিভিন্ন অংশগুলির একটি তালিকা দিয়ে, যেমনটি Listing 7-18-এ দেখানো হয়েছে।

use rand::Rng;
// --snip--
use std::{cmp::Ordering, io};
// --snip--

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess: u32 = guess.trim().parse().expect("Please type a number!");

    println!("You guessed: {guess}");

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}```

</Listing>

বড় প্রোগ্রামগুলিতে, নেস্টেড পাথ ব্যবহার করে একই ক্রেট বা মডিউল থেকে অনেক আইটেম স্কোপে আনা পৃথক `use` স্টেটমেন্টের সংখ্যা অনেক কমাতে পারে!

আমরা একটি পাথের যেকোনো স্তরে একটি নেস্টেড পাথ ব্যবহার করতে পারি, যা দুটি `use` স্টেটমেন্টকে একত্রিত করার সময় উপযোগী হয় যেগুলোর একটি সাবপাথ শেয়ার করা থাকে। উদাহরণস্বরূপ, Listing 7-19 দুটি `use` স্টেটমেন্ট দেখায়: একটি যা `std::io`-কে স্কোপে নিয়ে আসে এবং আরেকটি যা `std::io::Write`-কে স্কোপে নিয়ে আসে।

<Listing number="7-19" file-name="src/lib.rs" caption="দুটি `use` স্টেটমেন্ট যেখানে একটি অন্যটির সাবপাথ">

```rust,noplayground
use std::io;
use std::io::Write;

এই দুটি পাথের সাধারণ অংশ হলো std::io, এবং এটিই প্রথম সম্পূর্ণ পাথ। এই দুটি পাথকে একটি use স্টেটমেন্টে একত্রিত করতে, আমরা নেস্টেড পাথে self ব্যবহার করতে পারি, যেমনটি Listing 7-20-এ দেখানো হয়েছে।

use std::io::{self, Write};

এই লাইনটি std::io এবং std::io::Write-কে স্কোপে নিয়ে আসে।

গ্লোব অপারেটর

যদি আমরা একটি পাথে ডিফাইন করা সমস্ত পাবলিক আইটেমকে স্কোপে আনতে চাই, আমরা সেই পাথ এবং তারপরে * গ্লোব অপারেটরটি নির্দিষ্ট করতে পারি:

#![allow(unused)]
fn main() {
use std::collections::*;
}

এই use স্টেটমেন্টটি std::collections-এ ডিফাইন করা সমস্ত পাবলিক আইটেমকে বর্তমান স্কোপে নিয়ে আসে। গ্লোব অপারেটর ব্যবহার করার সময় সতর্ক থাকুন! গ্লোব ব্যবহার করলে কোন নামগুলো স্কোপে আছে এবং আপনার প্রোগ্রামে ব্যবহৃত একটি নাম কোথা থেকে ডিফাইন করা হয়েছে তা বোঝা কঠিন হয়ে যেতে পারে। উপরন্তু, যদি ডিপেন্ডেন্সি তার ডেফিনিশন পরিবর্তন করে, তাহলে আপনি যা ইম্পোর্ট করেছেন তাও পরিবর্তিত হবে, যা ডিপেন্ডেন্সি আপগ্রেড করার সময় কম্পাইলার এররের কারণ হতে পারে, যদি ডিপেন্ডেন্সি আপনার একই স্কোপের কোনো ডেফিনিশনের মতো একই নামের একটি ডেফিনিশন যোগ করে, উদাহরণস্বরূপ।

গ্লোব অপারেটরটি প্রায়শই টেস্টিংয়ের সময় tests মডিউলের মধ্যে পরীক্ষার অধীনে থাকা সমস্ত কিছু আনতে ব্যবহৃত হয়; আমরা এটি সম্পর্কে অধ্যায় ১১-এর “কিভাবে টেস্ট লিখতে হয়” অংশে কথা বলব। গ্লোব অপারেটরটি কখনও কখনও প্রিলিউড প্যাটার্নের অংশ হিসাবেও ব্যবহৃত হয়: সেই প্যাটার্ন সম্পর্কে আরও তথ্যের জন্য স্ট্যান্ডার্ড লাইব্রেরির ডকুমেন্টেশন দেখুন।