ruff/crates/ruff_cache/tests/cache_key.rs
Micha Reiser cdbe2ee496
refactor: Introduce CacheKey trait (#3323)
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.
2023-03-03 18:29:49 +00:00

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());
}