Rust – il linguaggio – 29

tavern_scene_woodcut1

Continuo da qui, oggi sono qui: /usr/local/share/doc/rust/html/book/crates-and-modules.html, al tag importing-external-crates.

Importare crates esterni

We have a library crate. Let’s make an executable crate that imports and uses our library.

Make a src/main.rs and put this in it (it won’t quite compile yet):

extern crate phrases;

fn main() {
    println!("Hello in English: {}", phrases::english::greetings::hello());
    println!("Goodbye in English: {}", phrases::english::farewells::goodbye());

    println!("Hello in Japanese: {}", phrases::japanese::greetings::hello());
    println!("Goodbye in Japanese: {}", phrases::japanese::farewells::goodbye());
}

The extern crate declaration tells Rust that we need to compile and link to the phrases crate. We can then use phrases’ modules in this one. As we mentioned earlier, you can use double colons to refer to sub-modules and the functions inside of them.

Note: when importing a crate that has dashes in its name “like-this”, which is not a valid Rust identifier, it will be converted by changing the dashes to underscores, so you would write extern crate like_this;.

Also, Cargo assumes that src/main.rs is the crate root of a binary crate, rather than a library crate. Our package now has two crates: src/lib.rs and src/main.rs. This pattern is quite common for executable crates: most functionality is in a library crate, and the executable crate uses that library. This way, other programs can also use the library crate, and it’s also a nice separation of concerns.

This doesn’t quite work yet, though. We get four errors that look similar to this:

$ cargo build
   Compiling phrases v0.0.1 (file:///home/you/projects/phrases)
src/main.rs:4:38: 4:72 error: function `hello` is private
src/main.rs:4     
println!("Hello in English: {}", phrases::english::greetings::hello());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: in expansion of format_args!
:2:25: 2:58 note: expansion site
:1:1: 2:62 note: in expansion of print!
:3:1: 3:54 note: expansion site
:1:1: 3:58 note: in expansion of println!
phrases/src/main.rs:4:5: 4:76 note: expansion site

Nota: non metto lo screenshot perché troppo lungo; questo è solo una parte dell’output.

By default, everything is private in Rust. Let’s talk about this in some more depth.

Esportare un’interfaccia pubblica

Rust allows you to precisely control which aspects of your interface are public, and so private is the default. To make things public, you use the pub keyword. Let’s focus on the english module first, so let’s reduce our src/main.rs to just this:

extern crate phrases;

fn main() {
    println!("Hello in English: {}", phrases::english::greetings::hello());
    println!("Goodbye in English: {}", phrases::english::farewells::goodbye());
}

In our src/lib.rs, let’s add pub to the english module declaration:

fn main() {
    pub mod english;
    mod japanese;
}

And in our src/english/mod.rs, let’s make both pub:

fn main() {
    pub mod greetings;
    pub mod farewells;
}

In our src/english/greetings.rs, let’s add pub to our fn declaration:

fn main() {
    pub fn hello() -> String {
        "Hello!".to_string()
    }
}

And also in src/english/farewells.rs:

fn main() {
    pub fn goodbye() -> String {
        "Goodbye.".to_string()
    }
}

Now, our crate compiles, albeit with warnings about not using the japanese functions:

$ cargo run
   Compiling phrases v0.0.1 (file:///home/you/projects/phrases)
src/japanese/greetings.rs:1:1: 3:2 warning: 
function is never used: `hello`, #[warn(dead_code)] on by default
src/japanese/greetings.rs:1 fn hello() -> String {
src/japanese/greetings.rs:2     "こんにちは".to_string()
src/japanese/greetings.rs:3 }
src/japanese/farewells.rs:1:1: 3:2 warning: 
function is never used: `goodbye`, #[warn(dead_code)] on by default
src/japanese/farewells.rs:1 fn goodbye() -> String {
src/japanese/farewells.rs:2     "さようなら".to_string()
src/japanese/farewells.rs:3 }
     Running `target/debug/phrases`
Hello in English: Hello!
Goodbye in English: Goodbye.

Come sopra; inoltre trovo anche:

src/lib.rs:2:5: 2:21 error: 
visibility has no effect inside functions or block expressions [E0447]
src/lib.rs:2     pub mod english;
                 ^~~~~~~~~~~~~~~~

pub also applies to structs and their member fields. In keeping with Rust’s tendency toward safety, simply making a struct public won’t automatically make its members public: you must mark the fields individually with pub.

Now that our functions are public, we can use them. Great! However, typing out phrases::english::greetings::hello() is very long [sembra quasi Java :wink:] and repetitive. Rust has another keyword for importing names into the current scope, so that you can refer to them with shorter names. Let’s talk about use.

Importare moduli con use

Rust has a use keyword, which allows us to import names into our local scope. Let’s change our src/main.rs to look like this:

extern crate phrases;

use phrases::english::greetings;
use phrases::english::farewells;

fn main() {
    println!("Hello in English: {}", greetings::hello());
    println!("Goodbye in English: {}", farewells::goodbye());
}

The two use lines import each module into the local scope, so we can refer to the functions by a much shorter name. By convention, when importing functions, it’s considered best practice to import the module, rather than the function directly. In other words, you can do this:

extern crate phrases;         // NO, nope!

use phrases::english::greetings::hello;
use phrases::english::farewells::goodbye;

fn main() {
    println!("Hello in English: {}", hello());
    println!("Goodbye in English: {}", goodbye());
}

But it is not idiomatic. This is significantly more likely to introduce a naming conflict. In our short program, it’s not a big deal, but as it grows, it becomes a problem. If we have conflicting names, Rust will give a compilation error. For example, if we made the japanese functions public, and tried to do this:

extern crate phrases;         // NO, nope!

use phrases::english::greetings::hello;
use phrases::japanese::greetings::hello;

fn main() {
    println!("Hello in English: {}", hello());
    println!("Hello in Japanese: {}", hello());
}

Rust will give us a compile-time error:

Compiling phrases v0.0.1 (file:///home/you/projects/phrases)
src/main.rs:4:5: 4:40 error: 
a value named `hello` has already been imported in this module [E0252]
src/main.rs:4 use phrases::japanese::greetings::hello;
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `phrases`.

If we’re importing multiple names from the same module, we don’t have to type it out twice. Instead of this:

fn main() {
    use phrases::english::greetings;
    use phrases::english::farewells;
}

We can use this shortcut:

fn main() {
    use phrases::english::{greetings, farewells};
}

Ri-esportare con pub use

You don’t just use use to shorten identifiers. You can also use it inside of your crate to re-export a function inside another module. This allows you to present an external interface that may not directly map to your internal code organization.

Let’s look at an example. Modify your src/main.rs to read like this:

extern crate phrases;

use phrases::english::{greetings,farewells};
use phrases::japanese;

fn main() {
    println!("Hello in English: {}", greetings::hello());
    println!("Goodbye in English: {}", farewells::goodbye());

    println!("Hello in Japanese: {}", japanese::hello());
    println!("Goodbye in Japanese: {}", japanese::goodbye());
}

Then, modify your src/lib.rs to make the japanese mod public:

fn main() {
    pub mod english;
    pub mod japanese;
}

Next, make the two functions public, first in src/japanese/greetings.rs:

fn main() {
    pub fn hello() -> String {
        "こんにちは".to_string()
    }
}

And then in src/japanese/farewells.rs:

fn main() {
    pub fn goodbye() -> String {
        "さようなら".to_string()
    }
}

Finally, modify your src/japanese/mod.rs to read like this:

fn main() {
    pub use self::greetings::hello;
    pub use self::farewells::goodbye;
    
    mod greetings;
    mod farewells;
}

The pub use declaration brings the function into scope at this part of our module hierarchy. Because we’ve pub used this inside of our japanese module, we now have a phrases::japanese::hello() function and a phrases::japanese::goodbye() function, even though the code for them lives in phrases::japanese::greetings::hello() and phrases::japanese::farewells::goodbye(). Our internal organization doesn’t define our external interface.

Here we have a pub use for each function we want to bring into the japanese scope. We could alternatively use the wildcard syntax to include everything from greetings into the current scope: pub use self::greetings::*.

What about the self? Well, by default, use declarations are absolute paths, starting from your crate root. self makes that path relative to your current place in the hierarchy instead. There’s one more special form of use: you can use super:: to reach one level up the tree from your current location. Some people like to think of self as . and super as .., from many shells’ display for the current directory and the parent directory.

Outside of use, paths are relative: foo::bar() refers to a function inside of foo relative to where we are. If that’s prefixed with ::, as in ::foo::bar(), it refers to a different foo, an absolute path from your crate root.

This will build and run:

$ cargo run
   Compiling phrases v0.0.1 (file:///home/you/projects/phrases)
     Running `target/debug/phrases`
Hello in English: Hello!
Goodbye in English: Goodbye.
Hello in Japanese: こんにちは
Goodbye in Japanese: さようなら

😦 Uhmmm… a me da un errore su src/japanese/mod.rs che non riesco a stanare (mi sembra tutto come da istruzioni), pausa mentre indago 😦

:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: