[Rust] 모듈시스템과 계층구조 (File hierarchy)

Rust의 모듈시스템을 사용하는 방법을 간단히 정리해본다.

소스코드를 파일이나 폴더 단위로 분리해서 관리하고, import해서 사용하는 방법들을 포함한다.




단일 파일 import하기: 현재 경로

파일의 import는 mod 키워드를 통해 수행할 수 있다.
만약 main에서 동일 경로에 있는 foo.rs를 가져오고 싶다면,

이렇게 파일명으로 mod를 거는 것으로 가져올 수 있다.

다만, 이건 동일 경로에 있는 것만 가능하다는 점을 기억하자.




단일 파일 import하기: 임의 경로

근데 가져오고싶은 코드가 동일 경로가 아니라, 뭔가 이상한 상위경로에 있거나, 중첩된 디렉터리에 놓고 싶을 수도 있다.
그럴때도 방법이 있는데

만약 이런식으로 가져오고 싶은 bar.rs라는 소스코드가 있다고 가정하면

path 매크로를 이용해서 가져올 경로를 수동으로 지정하는 방법이 있다.
경로는 현재 소스파일을 기준으로 한다.

가끔은 사용할만하다.




디렉터리 import: mod.rs 사용

근데, 대부분의 언어에서는 소스코드를 같은 그룹으로 묶어서 별도의 디렉터리로 관리하는 경우가 대부분이다.

위에서 소개한 임의경로 import로도 디렉터리 import를 할 수 있긴 하지만 권장되는 방법은 아니고, 서브디렉터리를 import하는 표준적인 방법이 2가지 있다.

첫번째 방법은 디렉터리마다 mod.rs라는 중간 파일을 만드는 것이다.
이게 조금 더 옛날 버전이라는 것 같다.

그리고 mod.rs에서 해당 디렉터리의 소스코드들을 전부 import하고,

폴더명으로 mod를 하면 된다.

Rust의 모듈시스템은 폴더명으로 mod를 했을 경우, 해당 폴더 안에 있는 mod.rs를 자동으로 읽어온다.




디렉터리 import: 동명의 *.rs 사용

혹은, mod.rs를 아예 사용하지 않는 방법도 있다.
mod.rs를 지워버리고, 해당 디렉터리와 동일한 이름의 소스파일을 그 상위에 두는 것이다.

그럼 저 array.rs라는 소스코드는, array/mod.rs와 완전히 동일한 모듈 컨텍스트를 갖는다.
그래서 똑같이 mod를 해오면 main에서 동일하게 가져올 수 있다.

위치와 이름만 다를 뿐이지 역할이나 기능은 완전히 동일하다고 보면 된다.




서브모듈간 참조

서브모듈로 하위시스템들을 잘 쪼개서 분리했다고 치자.
근데 한 프로젝트에서, 서브모듈끼리 서로를 import해서 참조해야할 일이 있으면 어떻게 해야할까?

예를 들어보겠다.
이렇게 array 서브모듈과 list 서브모듈이 나뉘어있다.

그냥 보면 잘 나눴다고 생각할 수 있다.
당연히 array와 list는 독립적으로 동작할 테니까.

하지만 경우에 따라서는 아닐 수도 있다.
만약 array -> list 에 대한 변환을 구현한다고 하면 이런 코드를 list 서브모듈 내에 구현해야 할 것이다.

그러려면 list 모듈에서 array 모듈을 참조해야 한다.
어떻게 해야할까?

이런 무식한 방법을 떠올릴 수도 있는데

이건 좋은 방법이 아니다.
잊지 말자. 모듈 컨텍스트에서 하나의 소스코드는 무조건 한번만 import가 되어야 한다.


그런데 main에서 array를 import하고, main에서 import한 list에서 array를 또 import하니 중복 import가 발생한 것이다.

만약 위처럼 중복 import error가 발생하지 않더라도, 문제는 여전하다. import된 버전마다 완전히 다른 소스코드로 인식하기 때문이다.
이럴 경우에는 a.rs에서 import한 DynamicArray와 b.rs가 import한 DynamicArray가 완전히 다른 이름으로 mangling돼서 타입 불일치가 발생한다.

그래서 권장하는 방법은 이렇다.
일단 서브모듈들이 만나는 종착지가 있지 않겠는가?
내 경우에는 main.rs가 최상위 모듈이 된다.

사실 이렇게 된 시점에서, array와 list모듈은 이미 상위 모듈인 main 모듈을 통해 연결이 된 상태다.
use만 하면 되는 것이다.

이런식으로 super를 사용해 main 모듈까지 올라가서 use를 해오거나

최상위 모듈을 뜻하는 crate를 통해 use해서 사용할 수가 있다.


참조
https://doc.rust-lang.org/rust-by-example/mod/split.html