프로그래밍백엔드 및 데이터 개발자

해시 및 Eq가 Rust의 사용자 정의 유형에 어떻게 구현되는지 설명하십시오. HashMap/HashSet에서 사용 시 발생할 수 있는 오류는 무엇인지, 그리고 그 오류가 왜 치명적인지 설명하십시오.

Hintsage AI 어시스턴트로 면접 통과

답변

사용자 정의 구조체를 HashMap 또는 HashSet과 같은 컬렉션에서 사용하기 위해서는 EqHash 트레이트(그리고 자주 PartialEq도) 를 구현해야 합니다. 이러한 트레이트를 구현하는 것은 객체가 서로 비교되는 방식과 해시가 계산되는 방식을 정의합니다.

중요: 두 객체가 같다면 (a == b), 그들의 해시 값은 일치해야 합니다 (hash(a) == hash(b)). 그렇지 않으면 해시에 의존하는 컨테이너가 올바르게 작동하지 않으며(요소를 찾거나 삭제할 수 없음), 오류가 발생합니다.

구현 예:

use std::hash::{Hash, Hasher}; #[derive(Eq, PartialEq)] struct Point { x: i32, y: i32, } impl Hash for Point { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); self.y.hash(state); } }

트릭 질문

Equal 비교(Eq/PartialEq)에 참여하는 필드와 해시에만 참여하는 필드를 포함하는 구조체에 대해 Hash를 구현할 수 있습니까?

답변: 아니요, 이것은 불변성을 위반하게 됩니다: 두 요소가 같다고 간주되는 경우 그들의 해시는 반드시 같아야 합니다. PartialEq/Eq에 참여하는 모든 필드는 Hash에서도 고려되어야 합니다. "비교되지 않는" 필드를 무시하는 것은 안전하지만, 그것들이 비교 논리에 참여하지 않는 경우에 한합니다.

impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // self.x가 Eq에서도 사용되는 경우에만 } }

이 주제에 대한 잘못된 이해로 인한 실제 오류 사례


이야기

대규모 서버 프로젝트에서 키 구조체가 HashMap에 사용되었지만 해시 계산에서 PartialEq에 참여하는 필드를 잊어버렸습니다. 그 결과, 필요한 키를 찾을 수 없게 되어 캐시에서 사용자 "유출" 문제가 발생했습니다: 요소는 같다고 간주되었지만 실제로는 달랐습니다.


이야기

오픈 소스 라이브러리에서 기하학적 객체의 HashEq에서는 부동 소수점 비교(f64)를 사용했습니다. 그 결과 NaN, -0.0/+0.0의 비교 특성으로 인해 표준 컨테이너가 잘못 작동하여 때때로 컬렉션에서 존재하는 객체를 찾지 못했습니다.


이야기

한 핀테크 프로젝트에서 리팩토링 시 비공개 필드를 포함한 구조체에 대해 자동 파생 #[derive(Hash, Eq, PartialEq)]를 사용하고, 선언 순서를 변경했습니다. 그 결과 최종 해시가 변경되어 캐시 기능이 파손되고 재시작 후 데이터 손실이 발생했습니다.