You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 14:25:23