Rust에서 살펴본 “검증하지 말고 파싱하라”

1 week ago 12

“검증하지 말고 파싱하라”는 데이터를 검사하는 데 그치지 않고, 검사로 확립한 불변 조건을 새로운 타입에 담아 이후 코드가 그 조건을 다시 확인할 필요를 없애는 패턴임 비어 있지 않음을 검사해도 반환 타입이 Vec이면 호출자는 여전히 빈 경우를 처리해야 하지만, NonEmpty<T> 를 반환하면 first()로 Option 없이 첫 원소를 얻을 수 있음 rust-analyzer의 AbsPathBuf 는 경로가 유효한 UTF-8인지, 절대 경로인지 단계적으로 확인하고 각 조건을 타입에 기록하는 점진적 파싱 사례임 표준 라이브러리의 NonZero 는 0이 아니라는 조건을 보장하며, Option<NonZeroUsize>는 0의 비트 패턴을 None에 활용해 usize와 같은 크기와 정렬을 유지함 serde의 JSON 역직렬화는 필드 타입, 열거형의 허용 값, 0이 아닌 정수 등의 조건을 파싱 과정에서 확인하고 결과 타입에 담는 일상적인 활용 사례임 검사만으로는 타입에 남지 않는 불변 조건 Alexis King의 Parse, don't validate는 익숙하지만 별도의 이름 없이 쓰이던 패턴을 정리한 글이며, 원문의 Haskell 사례를 Rust 표준 라이브러리와 실제 프로젝트에도 적용할 수 있음 Vec::first() 는 벡터가 비어 있을 수 있으므로 Option<&T>를 반환함 Rust에서는 Option을 반환하고 그 결과에 따라 다음 동작을 결정하는 방식이 관용적임 Go나 Python에서는 빈 슬라이스나 리스트에 lst[0]으로 접근하면 런타임 검사로 패닉이나 예외가 발생함 환경 변수 CONFIG_DIRS 를 읽어 쉼표로 분리하고, 공백을 제거한 뒤 PathBuf로 변환하는 함수가 비어 있지 않음을 검사해도, 반환 타입이 Result<Vec<PathBuf>>이면 그 조건은 타입에 남지 않음 호출자는 first() 결과를 다시 분기하고, None에는 unreachable! 같은 처리를 넣어야 함 이런 중복 처리는 코드 명료성을 해치고 잠재적으로 성능에 영향을 줄 수 있으며, 생성 함수의 불변 조건이 바뀌면 문제가 될 수 있음 근본적인 문제는 Vec가 빈 상태를 허용하는 타입이라는 점임 “이 벡터는 비어 있지 않다”는 주석을 붙여도 이를 형식적으로 확인하는 장치는 없음 비어 있지 않은 벡터를 타입으로 만들기 nonempty 크레이트의 NonEmpty<...

Read Entire Article