为何Python与Rust同哈希函数对同一字符串输出不同结果?
问题描述
我正在构建一个包含Rust和Python模块的系统,需要两端对同一输入生成相同的哈希值。了解到Python和Rust都支持SipHash 1-3,所以尝试用这个算法,但两端的哈希结果始终不一致。
初始尝试代码
Python端
>>> import ctypes >>> ctypes.c_size_t(hash(b'abcd')).value 14608482441665817778 >>> getsizeof(ctypes.c_size_t(hash(b'abcd')).value) 36 >>> type(b'abcd') <class 'bytes'>
Rust端
use hashers::{builtin::DefaultHasher}; use std::hash::{Hash, Hasher}; pub fn hash_str(s: &str) -> u64 { let mut hasher = DefaultHasher::new(); s.hash(&mut hasher); hasher.finish() } pub fn hash_bytes(b: &[u8]) -> u64 { let mut hasher = DefaultHasher::new(); b.hash(&mut hasher); hasher.finish() } fn test_hash_str() { let s1: &str = "abcd"; let h1: u64 = hash_str(s1); assert_eq!(h1, 13543138095457285553); } #[test] fn test_hash_bytes() { let b1: &[u8] = "abcd".as_bytes(); let h1: u64 = hash_bytes(b1); assert_eq!(h1, 18334232741324577590); }
后续排查
发现Python的hash()函数每次运行会用随机盐,设置PYTHONHASHSEED=0禁用后,结果还是和Rust不一致。改用自定义SipHash实现后:
- Rust端:
String和&str的哈希结果一致,但与[u8]数组的结果不同 - Python端:用SipHash1-3计算"abcd"的结果是
16416137402921954953,仍和Rust的两个结果都不匹配
解决方案
要让两端生成相同哈希,必须保证输入字节序列完全一致,且SipHash的初始化密钥、输入处理逻辑完全对齐。
1. 对齐SipHash的密钥
Rust的DefaultHasher默认使用固定密钥,但自定义实现时需显式指定,确保和Python端一致。
2. 统一输入处理逻辑
Rust中str和[u8]的Hash trait实现逻辑不同:
str会先写入字符串长度,再写入字节内容[u8]直接写入字节内容
以下两种方案任选其一:
方案A:直接哈希原始字节
两端都以原始字节数组为输入,跳过语言内置的类型哈希逻辑。
Rust代码
use siphasher::sip::SipHasher13; use std::hash::Hasher; pub fn hash_raw_bytes(data: &[u8]) -> u64 { // 使用固定密钥,和Python端保持一致 let mut hasher = SipHasher13::new_with_keys(0, 0); hasher.write(data); hasher.finish() } #[test] fn test_raw_bytes() { let bytes = "abcd".as_bytes(); assert_eq!(hash_raw_bytes(bytes), 16416137402921954953); // 匹配Python结果 }
Python代码
from siphasher import SipHash # 使用和Rust相同的密钥 sh = SipHash(c=1, d=3, k0=0, k1=0) h = sh.auth(0, b"abcd") assert h == 16416137402921954953
方案B:模拟Rust的字符串哈希逻辑
如果需要对齐Rust中str的哈希方式,Python端需先写入字符串长度(u64类型),再写入字节内容。
Rust代码
use siphasher::sip::SipHasher13; use std::hash::{Hash, Hasher}; pub fn hash_str_rust(s: &str) -> u64 { let mut hasher = SipHasher13::new_with_keys(0, 0); s.hash(&mut hasher); hasher.finish() } #[test] fn test_str() { assert_eq!(hash_str_rust("abcd"), 13543138095457285553); }
Python代码
from siphasher import SipHash import struct def hash_str_rust_style(s: str) -> int: sh = SipHash(c=1, d=3, k0=0, k1=0) data = s.encode("utf-8") # 先写入字符串长度(小端字节序,匹配Rust的usize处理) length_bytes = struct.pack("<Q", len(data)) sh.update(length_bytes) sh.update(data) return sh.finish() assert hash_str_rust_style("abcd") == 13543138095457285553
关键注意事项
- 不要使用语言内置的
hash()函数:Python的hash()有随机盐和类型特殊处理,Rust的DefaultHasher因类型差异无法跨语言对齐。 - 统一编码:字符串必须统一用UTF-8编码为字节数组,避免编码差异。
- 固定密钥:两端必须使用完全相同的SipHash初始化密钥,不能依赖默认值。
内容的提问来源于stack exchange,提问作者Istvan
相关产品推荐
相关产品推荐

