러스트에는 C++나 자바와 같은 전통적인 생성자가 없지만, 객체 유형을 생성할 때 일반적으로 연관된 함수(종종 new라는 이름을 사용함)와 이른바 팩토리 메소드를 사용합니다. 이는 언어의 역사와 관련이 있는데, 여기서 초기화의 안전성과 명시성이 강조됩니다. 명시적으로 작성하고 호출되는 함수만이 구조체의 각각의 필드를 올바르게 초기화하는 책임이 있습니다.
문제의 역사
초기에는 러스트에서 구조체 초기화가 모든 필드에 대한 직접 할당을 허용했습니다(소위 "struct literal" 문법). 하지만 불변성을 보장하고 세부 사항을 숨기며 추가 검사를 구현하기 위해, 팩토리 메소드(impl SomeStruct { fn new(...) -> Self { ... } })를 사용하는 것이 일반적이며, 심지어 템플릿을 통한 일반화(빌더 패턴)도 이루어집니다.
문제
주요 과제는 부분적으로 초기화된 객체 생성을 방지하고 잘못된 상태의 구조체 사용을 불가능하게 만드는 것입니다. 이는 리소스와 관련된 복잡한 구조체(예: 파일, 소켓 등)의 경우 모든 필드를 수동으로 초기화할 경우 오류를 초래할 수 있기 때문에 특히 중요합니다.
해결책
러스트에서는 팩토리 메소드를 생성하는 것이 권장되며, 이는 완전히 초기화된 객체를 반환하고, 필요할 경우 유효성을 검사하며 인스턴스화 세부 사항을 숨깁니다.
코드 예제:
struct User { username: String, age: u8, } impl User { pub fn new(username: String, age: u8) -> Option<Self> { if age >= 18 { Some(Self { username, age }) } else { None } } } fn main() { let user = User::new("Alice".to_string(), 20); // user: Option<User>, 안전하게 오류 처리 }
주요 특징:
fn new)를 통한 구현이 있습니다.구조체에서 비공개 필드를 만들어 모듈 외부에서 직접 인스턴스를 생성할 수 없게 할 수 있습니까?
예, 구조체의 모든 필드를 비공개로 만들고 공개 팩토리 메소드만 제공하면 구조체는 모듈 외부에서 직접 초기화할 수 없습니다.
팩토리 메소드는 항상 new라고 불러야 합니까?
아니요, 이것은 관습일 뿐 의무는 아닙니다. 다양한 초기화 전략을 위해 "with_capacity", "from_config", "from_env"와 같은 이름을 사용할 수 있습니다.
비공식 생성자가 있을 수 있습니까?
예, 연관된 함수가 fn new(...) -> Self로 선언되고 pub 수정자가 없으면, 이 모듈 외부에서는 호출할 수 없습니다. 이는 예를 들어 싱글턴, 강제 팩토리 또는 숨겨진 초기화를 구현할 수 있게 해 줍니다.
팩토리 메소드 없이 공개 필드를 가진 구조체를 직접 사용하는 경우:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1은 유효하지 않은 디스크립터!
장점:
단점:
비공식 필드와 팩토리 메소드를 사용하는 경우:
pub struct Connection { fd: i32, timeout: u64, } impl Connection { pub fn new(fd: i32, timeout: u64) -> Option<Self> { if fd >= 0 { Some(Self { fd, timeout }) } else { None } } }
장점:
단점: