mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00

This PR introduces a new `CacheKey` trait for types that can be used as a cache key. I'm not entirely sure if this is worth the "overhead", but I was surprised to find `HashableHashSet` and got scared when I looked at the time complexity of the `hash` function. These implementations must be extremely slow in hashed collections. I then searched for usages and quickly realized that only the cache uses these `Hash` implementations, where performance is less sensitive. This PR introduces a new `CacheKey` trait to communicate the difference between a hash and computing a key for the cache. The new trait can be implemented for types that don't implement `Hash` for performance reasons, and we can define additional constraints on the implementation: For example, we'll want to enforce portability when we add remote caching support. Using a different trait further allows us not to implement it for types without stable identities (e.g. pointers) or use other implementations than the standard hash function.
108 lines
2.3 KiB
Rust
108 lines
2.3 KiB
Rust
use ruff_cache::{CacheKey, CacheKeyHasher};
|
|
use ruff_macros::CacheKey;
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::{Hash, Hasher};
|
|
|
|
#[derive(CacheKey, Hash)]
|
|
struct UnitStruct;
|
|
|
|
#[derive(CacheKey, Hash)]
|
|
struct NamedFieldsStruct {
|
|
a: String,
|
|
b: String,
|
|
}
|
|
|
|
#[derive(CacheKey, Hash)]
|
|
struct UnnamedFieldsStruct(String, String);
|
|
|
|
#[derive(CacheKey, Hash)]
|
|
enum Enum {
|
|
Unit,
|
|
UnnamedFields(String, String),
|
|
NamedFields { a: String, b: String },
|
|
}
|
|
|
|
#[test]
|
|
fn unit_struct_cache_key() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
UnitStruct.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
UnitStruct.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn named_field_struct() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
let named_fields = NamedFieldsStruct {
|
|
a: "Hello".into(),
|
|
b: "World".into(),
|
|
};
|
|
|
|
named_fields.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
named_fields.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn unnamed_field_struct() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
let unnamed_fields = UnnamedFieldsStruct("Hello".into(), "World".into());
|
|
|
|
unnamed_fields.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
unnamed_fields.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn enum_unit_variant() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
let variant = Enum::Unit;
|
|
variant.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
variant.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn enum_named_fields_variant() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
let variant = Enum::NamedFields {
|
|
a: "Hello".to_string(),
|
|
b: "World".to_string(),
|
|
};
|
|
variant.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
variant.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn enum_unnamed_fields_variant() {
|
|
let mut key = CacheKeyHasher::new();
|
|
|
|
let variant = Enum::UnnamedFields("Hello".to_string(), "World".to_string());
|
|
variant.cache_key(&mut key);
|
|
|
|
let mut hash = DefaultHasher::new();
|
|
variant.hash(&mut hash);
|
|
|
|
assert_eq!(hash.finish(), key.finish());
|
|
}
|