소개
use 키워드를 사용하여 경로를 범위 내로 가져오기에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.
이 랩에서는 use 키워드를 사용하여 경로를 범위 내로 가져와 함수와 모듈을 호출하기 위한 단축키를 만드는 방법을 배웁니다.
use 키워드를 사용하여 경로를 범위 내로 가져오기에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.
이 랩에서는 use 키워드를 사용하여 경로를 범위 내로 가져와 함수와 모듈을 호출하기 위한 단축키를 만드는 방법을 배웁니다.
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만 지정하면 됩니다.
파일 이름: src/lib.rs
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();
}
Listing 7-11: use를 사용하여 모듈을 범위 내로 가져오기
범위에 use와 경로를 추가하는 것은 파일 시스템에서 심볼릭 링크를 만드는 것과 유사합니다. 크레이트 루트에 use crate::front_of_house::hosting을 추가하면 hosting은 해당 범위에서 유효한 이름이 됩니다. 마치 hosting 모듈이 크레이트 루트에 정의된 것처럼 말입니다. use를 사용하여 범위 내로 가져온 경로는 다른 경로와 마찬가지로 개인 정보 보호도 확인합니다.
use는 use가 발생하는 특정 범위에 대해서만 단축키를 생성합니다. Listing 7-12 는 eat_at_restaurant 함수를 customer라는 새로운 자식 모듈로 이동합니다. 이는 use 문과는 다른 범위이므로 함수 본문은 컴파일되지 않습니다.
파일 이름: src/lib.rs
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();
}
}
Listing 7-12: use 문은 해당 범위에서만 적용됩니다.
컴파일러 오류는 단축키가 더 이상 customer 모듈 내에서 적용되지 않음을 보여줍니다.
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`
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
use가 더 이상 해당 범위에서 사용되지 않는다는 경고도 있습니다! 이 문제를 해결하려면 use를 customer 모듈 내로 이동하거나, 자식 customer 모듈 내에서 super::hosting을 사용하여 상위 모듈의 단축키를 참조하십시오.
use 경로 생성Listing 7-11 에서 use crate::front_of_house::hosting을 지정한 다음 eat_at_restaurant에서 hosting::add_to_waitlist를 호출한 이유가 궁금했을 수 있습니다. Listing 7-13 과 같이 add_to_waitlist 함수까지 모든 경로를 지정하여 동일한 결과를 얻는 대신 말입니다.
파일 이름: src/lib.rs
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-13: 관용적이지 않은 방식으로 use를 사용하여 add_to_waitlist 함수를 범위 내로 가져오기
Listing 7-11 과 Listing 7-13 모두 동일한 작업을 수행하지만, Listing 7-11 은 use를 사용하여 함수를 범위 내로 가져오는 관용적인 방법입니다. 함수의 상위 모듈을 use로 범위 내로 가져오면 함수를 호출할 때 상위 모듈을 지정해야 합니다. 함수를 호출할 때 상위 모듈을 지정하면 함수가 로컬에서 정의되지 않았음을 명확하게 하면서 전체 경로의 반복을 최소화합니다. Listing 7-13 의 코드는 add_to_waitlist가 어디에 정의되어 있는지 불분명합니다.
반면에, 구조체, 열거형 및 기타 항목을 use로 가져올 때는 전체 경로를 지정하는 것이 관용적입니다. Listing 7-14 는 표준 라이브러리의 HashMap 구조체를 바이너리 크레이트의 범위 내로 가져오는 관용적인 방법을 보여줍니다.
파일 이름: src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
Listing 7-14: 관용적인 방식으로 HashMap을 범위 내로 가져오기
이 관용구 뒤에는 강력한 이유가 없습니다. 이는 단지 나타난 관례이며, 사람들은 이러한 방식으로 Rust 코드를 읽고 쓰는 데 익숙해졌습니다.
이 관용구의 예외는 use 문을 사용하여 동일한 이름을 가진 두 개의 항목을 범위 내로 가져오는 경우입니다. Rust 는 이를 허용하지 않기 때문입니다. Listing 7-15 는 동일한 이름을 갖지만 다른 상위 모듈을 가진 두 개의 Result 타입을 범위 내로 가져오고, 이를 참조하는 방법을 보여줍니다.
파일 이름: src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
--snip--
}
fn function2() -> io::Result<()> {
--snip--
}
Listing 7-15: 동일한 이름을 가진 두 개의 타입을 동일한 범위로 가져오려면 상위 모듈을 사용해야 합니다.
보시다시피, 상위 모듈을 사용하면 두 개의 Result 타입을 구별할 수 있습니다. 대신 use std::fmt::Result 및 use std::io::Result를 지정하면 동일한 범위에 두 개의 Result 타입이 있게 되며, Rust 는 Result를 사용할 때 어느 것을 의미하는지 알 수 없습니다.
as 키워드를 사용하여 새 이름 제공use를 사용하여 동일한 이름을 가진 두 개의 타입을 동일한 범위로 가져오는 문제에 대한 또 다른 해결책이 있습니다. 경로 뒤에 as와 새 로컬 이름, 즉 타입에 대한 *별칭 (alias)*을 지정할 수 있습니다. Listing 7-16 은 as를 사용하여 두 개의 Result 타입 중 하나를 이름을 변경하여 Listing 7-15 의 코드를 작성하는 또 다른 방법을 보여줍니다.
파일 이름: src/lib.rs
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
--snip--
}
fn function2() -> IoResult<()> {
--snip--
}
Listing 7-16: as 키워드를 사용하여 범위를 가져올 때 타입의 이름 변경
두 번째 use 문에서 std::io::Result 타입에 대한 새 이름으로 IoResult를 선택했습니다. 이는 또한 범위 내로 가져온 std::fmt의 Result와 충돌하지 않습니다. Listing 7-15 와 Listing 7-16 은 관용적인 것으로 간주되므로 선택은 여러분에게 달려 있습니다!
pub use를 사용하여 이름 재수출하기use 키워드를 사용하여 이름을 범위 내로 가져올 때, 새 범위에서 사용 가능한 이름은 private 입니다. 우리 코드를 호출하는 코드가 해당 코드의 범위에서 정의된 것처럼 해당 이름을 참조할 수 있도록 하려면 pub와 use를 결합할 수 있습니다. 이 기술을 *재수출 (re-exporting)*이라고 합니다. 이는 항목을 범위 내로 가져오는 동시에 다른 사람들이 자신의 범위로 가져올 수 있도록 해당 항목을 사용할 수 있게 하기 때문입니다.
Listing 7-17 은 루트 모듈에서 use를 pub use로 변경한 Listing 7-11 의 코드를 보여줍니다.
파일 이름: src/lib.rs
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();
}
Listing 7-17: pub use를 사용하여 모든 코드가 새 범위에서 사용할 수 있도록 이름 만들기
이 변경 전에는 외부 코드가 add_to_waitlist 함수를 호출하려면 경로 restaurant::front_of_house::hosting::add_to_waitlist()를 사용해야 했습니다. 이제 이 pub use가 루트 모듈에서 hosting 모듈을 재수출했으므로 외부 코드는 대신 경로 restaurant::hosting::add_to_waitlist()를 사용할 수 있습니다.
재수출은 코드의 내부 구조가 코드를 호출하는 프로그래머가 도메인에 대해 생각하는 방식과 다를 때 유용합니다. 예를 들어, 이 레스토랑 비유에서 레스토랑을 운영하는 사람들은 "front of house"와 "back of house"에 대해 생각합니다. 그러나 레스토랑을 방문하는 고객은 아마도 레스토랑의 부분을 그러한 용어로 생각하지 않을 것입니다. pub use를 사용하면 하나의 구조로 코드를 작성하지만 다른 구조를 노출할 수 있습니다. 이렇게 하면 라이브러리에서 작업하는 프로그래머와 라이브러리를 호출하는 프로그래머 모두에게 라이브러리가 잘 구성됩니다. "pub use 를 사용하여 편리한 공개 API 내보내기"에서 pub use의 또 다른 예와 이것이 크레이트의 문서에 미치는 영향을 살펴보겠습니다.
2 장에서 우리는 rand라는 외부 패키지를 사용하여 난수를 얻는 추측 게임 프로젝트를 프로그래밍했습니다. 프로젝트에서 rand를 사용하기 위해 Cargo.toml에 다음 줄을 추가했습니다.
파일 이름: Cargo.toml
rand = "0.8.5"
Cargo.toml에 rand를 종속성으로 추가하면 Cargo 가 https://crates.io에서 rand 패키지 및 모든 종속성을 다운로드하고 rand를 프로젝트에서 사용할 수 있도록 합니다.
그런 다음 rand 정의를 패키지 범위로 가져오기 위해 크레이트 이름인 rand로 시작하는 use 줄을 추가하고 범위로 가져오려는 항목을 나열했습니다. "난수 생성하기"에서 Rng 트레이트를 범위로 가져오고 rand::thread_rng 함수를 호출했음을 기억하십시오.
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);
}
Rust 커뮤니티의 구성원들은 https://crates.io에서 많은 패키지를 사용할 수 있도록 했으며, 이들 중 어떤 것이든 패키지로 가져오는 것은 다음과 같은 동일한 단계를 포함합니다. 패키지의 Cargo.toml 파일에 나열하고 use를 사용하여 크레이트에서 항목을 범위로 가져옵니다.
표준 std 라이브러리도 우리 패키지 외부의 크레이트라는 점에 유의하십시오. 표준 라이브러리는 Rust 언어와 함께 제공되므로 std를 포함하도록 Cargo.toml을 변경할 필요가 없습니다. 그러나 거기에서 항목을 패키지 범위로 가져오려면 use로 참조해야 합니다. 예를 들어, HashMap을 사용하면 다음 줄을 사용합니다.
use std::collections::HashMap;
이것은 표준 라이브러리 크레이트의 이름인 std로 시작하는 절대 경로입니다.
use 목록 정리하기동일한 크레이트 또는 동일한 모듈에 정의된 여러 항목을 사용하는 경우, 각 항목을 별도의 줄에 나열하면 파일에서 많은 수직 공간을 차지할 수 있습니다. 예를 들어, Listing 2-4 의 추측 게임에서 사용했던 다음 두 개의 use 문은 std에서 항목을 범위로 가져옵니다.
파일 이름: src/main.rs
--snip--
use std::cmp::Ordering;
use std::io;
--snip--
대신, 중첩 경로를 사용하여 동일한 항목을 한 줄로 범위로 가져올 수 있습니다. 이렇게 하려면 경로의 공통 부분을 지정한 다음 콜론 두 개를 입력하고, Listing 7-18 에 표시된 것처럼 경로의 다른 부분 목록을 중괄호로 묶습니다.
파일 이름: src/main.rs
--snip--
use std::{cmp::Ordering, io};
--snip--
Listing 7-18: 동일한 접두사를 가진 여러 항목을 범위로 가져오기 위해 중첩 경로 지정
더 큰 프로그램에서는 중첩 경로를 사용하여 동일한 크레이트 또는 모듈에서 많은 항목을 범위로 가져오면 필요한 별도의 use 문의 수를 크게 줄일 수 있습니다!
경로의 모든 수준에서 중첩 경로를 사용할 수 있으며, 이는 하위 경로를 공유하는 두 개의 use 문을 결합할 때 유용합니다. 예를 들어, Listing 7-19 는 두 개의 use 문을 보여줍니다. 하나는 std::io를 범위로 가져오고 다른 하나는 std::io::Write를 범위로 가져옵니다.
파일 이름: src/lib.rs
use std::io;
use std::io::Write;
Listing 7-19: 하나가 다른 하나의 하위 경로인 두 개의 use 문
이 두 경로의 공통 부분은 std::io이며, 이는 첫 번째 경로를 완성합니다. 이 두 경로를 하나의 use 문으로 병합하려면 Listing 7-20 에 표시된 것처럼 중첩 경로에서 self를 사용할 수 있습니다.
파일 이름: src/lib.rs
use std::io::{self, Write};
Listing 7-20: Listing 7-19 의 경로를 하나의 use 문으로 결합
이 줄은 std::io와 std::io::Write를 범위로 가져옵니다.
경로에 정의된 모든 public 항목을 범위로 가져오려면 해당 경로를 지정하고 그 뒤에 * glob 연산자를 사용할 수 있습니다.
use std::collections::*;
이 use 문은 std::collections에 정의된 모든 public 항목을 현재 범위로 가져옵니다. glob 연산자를 사용할 때는 주의하십시오! Glob 는 어떤 이름이 범위에 있는지, 그리고 프로그램에서 사용된 이름이 어디에서 정의되었는지 파악하기 어렵게 만들 수 있습니다.
glob 연산자는 테스트 시 테스트 아래의 모든 것을 tests 모듈로 가져오기 위해 자주 사용됩니다. "테스트 작성 방법"에서 이에 대해 이야기할 것입니다. glob 연산자는 또한 prelude 패턴의 일부로 사용되기도 합니다. 해당 패턴에 대한 자세한 내용은 표준 라이브러리 문서를 참조하십시오.
축하합니다! use 키워드를 사용하여 경로를 범위로 가져오기 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.