← Back to Reports Index

CELLO Evaluation Report: hashmap

Generated: 2026-02-16T12:12:48.696813

Original C Code (3297 bytes)

/* Simple hash map implementation - fundamental data structure */

#include <stdlib.h>
#include <string.h>

#define HASHMAP_INITIAL_CAPACITY 16

typedef struct HashMapEntry {
    char* key;
    void* value;
    struct HashMapEntry* next;
} HashMapEntry;

typedef struct {
    HashMapEntry** buckets;
    size_t capacity;
    size_t size;
} HashMap;

/**
 * Simple hash function (djb2)
 */
static unsigned long hash_string(const char* str) {
    unsigned long hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }
    return hash;
}

/**
 * Create a new hash map
 */
HashMap* hashmap_new(void) {
    HashMap* map = (HashMap*)malloc(sizeof(HashMap));
    if (!map) return NULL;
    
    map->capacity = HASHMAP_INITIAL_CAPACITY;
    map->size = 0;
    map->buckets = (HashMapEntry**)calloc(map->capacity, sizeof(HashMapEntry*));
    
    if (!map->buckets) {
        free(map);
        return NULL;
    }
    
    return map;
}

/**
 * Insert or update a key-value pair
 */
int hashmap_put(HashMap* map, const char* key, void* value) {
    if (!map || !key) return -1;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    // Check if key exists
    HashMapEntry* entry = map->buckets[index];
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            entry->value = value;
            return 0;
        }
        entry = entry->next;
    }
    
    // Create new entry
    HashMapEntry* new_entry = (HashMapEntry*)malloc(sizeof(HashMapEntry));
    if (!new_entry) return -1;
    
    new_entry->key = strdup(key);
    if (!new_entry->key) {
        free(new_entry);
        return -1;
    }
    
    new_entry->value = value;
    new_entry->next = map->buckets[index];
    map->buckets[index] = new_entry;
    map->size++;
    
    return 0;
}

/**
 * Get value for a key
 */
void* hashmap_get(HashMap* map, const char* key) {
    if (!map || !key) return NULL;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    HashMapEntry* entry = map->buckets[index];
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            return entry->value;
        }
        entry = entry->next;
    }
    
    return NULL;
}

/**
 * Remove a key-value pair
 */
int hashmap_remove(HashMap* map, const char* key) {
    if (!map || !key) return -1;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    HashMapEntry** indirect = &map->buckets[index];
    HashMapEntry* entry = *indirect;
    
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            *indirect = entry->next;
            free(entry->key);
            free(entry);
            map->size--;
            return 0;
        }
        indirect = &entry->next;
        entry = entry->next;
    }
    
    return -1;
}

/**
 * Free hash map and all entries
 */
void hashmap_free(HashMap* map) {
    if (!map) return;
    
    for (size_t i = 0; i < map->capacity; i++) {
        HashMapEntry* entry = map->buckets[i];
        while (entry) {
            HashMapEntry* next = entry->next;
            free(entry->key);
            free(entry);
            entry = next;
        }
    }
    
    free(map->buckets);
    free(map);
}

CLAUDE Transpilation

Overall Score: 53/100
Dimension Score Max Details
Compilation 0 25 ✗ Compilation errors: warning: unused import: `std::collections::hash_map::DefaultHasher` --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:1:5 | 1 | use std::collections::hash_map::DefaultHasher; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default warning: unused imports: `Hash` and `Hasher` --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:2:17 | 2 | use std::hash::{Hash, Hasher}; | ^^^^ ^^^^^^ error[E0506]: cannot assign to `*current` because it is borrowed --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:118:25 | 116 | Some(entry) => { | ----- `*current` is borrowed here 117 | if entry.key == key { 118 | *current = entry.next.take(); | ^^^^^^^^ | | | `*current` is assigned to here but it was already borrowed | borrow later used here error: aborting due to 1 previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0506`.
Safety 20 20 ✓ No unsafe blocks | ✓ Uses Result for error handling | ✓ Uses Option for nullable values | ✓ Smart pointer usage
Quality 15 20 ✓ Snake_case naming | ✓ Well documented (10 doc comments) | ✓ Functional programming patterns | ✓ Proper type definitions
Correctness 3 15 ✓ Pattern matching
Maintainability 5 10 ✓ Clear public API (8 items) | ✓ Uses constants
Performance 10 10 ✓ Minimal cloning | ✓ Reference usage

❌ Compilation: FAILED

Compilation Errors

✗ Compilation errors: warning: unused import: `std::collections::hash_map::DefaultHasher`
 --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:1:5
  |
1 | use std::collections::hash_map::DefaultHasher;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default

warning: unused imports: `Hash` and `Hasher`
 --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:2:17
  |
2 | use std::hash::{Hash, Hasher};
  |                 ^^^^  ^^^^^^

error[E0506]: cannot assign to `*current` because it is borrowed
   --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpnhftkqi6/lib.rs:118:25
    |
116 |                 Some(entry) => {
    |                      ----- `*current` is borrowed here
117 |                     if entry.key == key {
118 |                         *current = entry.next.take();
    |                         ^^^^^^^^
    |                         |
    |                         `*current` is assigned to here but it was already borrowed
    |                         borrow later used here

error: aborting due to 1 previous error; 2 warnings emitted

For more information about this error, try `rustc --explain E0506`.

🔍 Failure Analysis

Error Type: E0506: Borrow Checker Violation

Summary: Cannot assign to a variable because it is borrowed

Location: Line 1: `use std::collections::hash_map::DefaultHasher;`

What Went Wrong

Rust's borrow checker prevents you from modifying a variable while you have an active borrow of it. In this case, the code tries to assign to a mutable reference (`*current`) while `entry` still borrows it. This is a classic pattern when working with self-referential data structures like linked lists.

Root Cause

The LLM translated C's pointer manipulation patterns directly to Rust, but didn't account for Rust's ownership rules. C allows you to modify pointers freely, but Rust tracks borrows statically and prevents simultaneous aliasing + mutation.

How to Fix It

Use `std::mem::replace()` or restructure the code to avoid borrowing and mutating simultaneously:
```rust
// Instead of:
*current = entry.next.take();

// Use:
let next = std::mem::replace(current, None);
*current = next.and_then(|mut e| e.next.take());
```
Or use `Option::take()` earlier to move ownership before the borrow.

Generated Rust Code (3738 bytes)

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

const HASHMAP_INITIAL_CAPACITY: usize = 16;

/// Entry in the hash map containing a key-value pair and pointer to next entry
struct HashMapEntry<T> {
    key: String,
    value: T,
    next: Option<Box<HashMapEntry<T>>>,
}

/// Simple hash map implementation using separate chaining
pub struct HashMap<T> {
    buckets: Vec<Option<Box<HashMapEntry<T>>>>,
    capacity: usize,
    size: usize,
}

/// Simple hash function (djb2)
fn hash_string(s: &str) -> u64 {
    let mut hash: u64 = 5381;
    for c in s.bytes() {
        hash = hash.wrapping_shl(5).wrapping_add(hash).wrapping_add(c as u64);
    }
    hash
}

impl<T> HashMap<T> {
    /// Create a new hash map
    pub fn new() -> Self {
        HashMap {
            buckets: (0..HASHMAP_INITIAL_CAPACITY).map(|_| None).collect(),
            capacity: HASHMAP_INITIAL_CAPACITY,
            size: 0,
        }
    }

    /// Insert or update a key-value pair
    pub fn put(&mut self, key: &str, value: T) -> Result<(), String> {
        let hash = hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &mut self.buckets[index];

        // Check if key exists and update
        loop {
            match current {
                Some(entry) => {
                    if entry.key == key {
                        entry.value = value;
                        return Ok(());
                    }
                    current = &mut entry.next;
                }
                None => break,
            }
        }

        // Create new entry
        let new_entry = Box::new(HashMapEntry {
            key: key.to_string(),
            value,
            next: self.buckets[index].take(),
        });

        self.buckets[index] = Some(new_entry);
        self.size += 1;

        Ok(())
    }

    /// Get value for a key
    pub fn get(&self, key: &str) -> Option<&T> {
        let hash = hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &self.buckets[index];

        while let Some(entry) = current {
            if entry.key == key {
                return Some(&entry.value);
            }
            current = &entry.next;
        }

        None
    }

    /// Get mutable value for a key
    pub fn get_mut(&mut self, key: &str) -> Option<&mut T> {
        let hash = hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &mut self.buckets[index];

        while let Some(entry) = current {
            if entry.key == key {
                return Some(&mut entry.value);
            }
            current = &mut entry.next;
        }

        None
    }

    /// Remove a key-value pair
    pub fn remove(&mut self, key: &str) -> Result<(), String> {
        let hash = hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &mut self.buckets[index];

        loop {
            match current {
                Some(entry) => {
                    if entry.key == key {
                        *current = entry.next.take();
                        self.size -= 1;
                        return Ok(());
                    }
                    current = &mut entry.next;
                }
                None => return Err("Key not found".to_string()),
            }
        }
    }

    /// Get the number of entries in the map
    pub fn size(&self) -> usize {
        self.size
    }

    /// Get the capacity of the map
    pub fn capacity(&self) -> usize {
        self.capacity
    }
}

impl<T> Default for HashMap<T> {
    fn default() -> Self {
        Self::new()
    }
}

GEMINI Transpilation

Overall Score: 52/100
Dimension Score Max Details
Compilation 0 25 ✗ Compilation errors: warning: unused import: `std::ptr` --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:5:5 | 5 | use std::ptr; | ^^^^^^^^ | = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default error[E0277]: the trait bound `Box<HashMapEntry<K, V>>: Clone` is not satisfied --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:43:28 | 43 | let buckets = vec![None; capacity]; | -----^^^^----------- | | | | | the trait `Clone` is not implemented for `Box<HashMapEntry<K, V>>` | required by a bound introduced by this call | = note: required for `Box<HashMapEntry<K, V>>` to implement `Clone` = note: 1 redundant requirement hidden = note: required for `Option<Box<HashMapEntry<K, V>>>` to implement `Clone` note: required by a bound in `from_elem` --> /rustc/01f6ddf7588f42ae2d7eb0a2f21d44e8e96674cf/library/alloc/src/vec/mod.rs:3509:1 help: consider borrowing here | 43 | let buckets = vec![&None; capacity]; | + error[E0506]: cannot assign to `current.next` because it is borrowed --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:86:17 | 71 | match &mut current.next { | ----------------- `current.next` is borrowed here ... 86 | current.next = Some(Box::new(new_entry)); | ^^^^^^^^^^^^ | | | `current.next` is assigned to here but it was already borrowed | borrow later used here error[E0506]: cannot assign to `current.next` because it is borrowed --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:138:29 | 136 | while let Some(next_entry) = &mut current.next { | ----------------- `current.next` is borrowed here 137 | if next_entry.key == *key { 138 | current.next = next_entry.next.take(); | ^^^^^^^^^^^^ | | | `current.next` is assigned to here but it was already borrowed | borrow later used here error: aborting due to 3 previous errors; 1 warning emitted Some errors have detailed explanations: E0277, E0506. For more information about an error, try `rustc --explain E0277`.
Safety 20 20 ✓ No unsafe blocks | ✓ Uses Result for error handling | ✓ Uses Option for nullable values | ✓ Smart pointer usage
Quality 17 20 ✓ Snake_case naming | ✓ Well documented (5 doc comments) | ✓ Uses ? operator | ✓ Proper type definitions | ✓ Implementation blocks
Correctness 3 15 ✓ Pattern matching
Maintainability 4 10 ✓ Uses constants | ✓ Type aliases
Performance 8 10 ✓ Minimal cloning

❌ Compilation: FAILED

Compilation Errors

✗ Compilation errors: warning: unused import: `std::ptr`
 --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:5:5
  |
5 | use std::ptr;
  |     ^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default

error[E0277]: the trait bound `Box<HashMapEntry<K, V>>: Clone` is not satisfied
  --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:43:28
   |
43 |         let buckets = vec![None; capacity];
   |                       -----^^^^-----------
   |                       |    |
   |                       |    the trait `Clone` is not implemented for `Box<HashMapEntry<K, V>>`
   |                       required by a bound introduced by this call
   |
   = note: required for `Box<HashMapEntry<K, V>>` to implement `Clone`
   = note: 1 redundant requirement hidden
   = note: required for `Option<Box<HashMapEntry<K, V>>>` to implement `Clone`
note: required by a bound in `from_elem`
  --> /rustc/01f6ddf7588f42ae2d7eb0a2f21d44e8e96674cf/library/alloc/src/vec/mod.rs:3509:1
help: consider borrowing here
   |
43 |         let buckets = vec![&None; capacity];
   |                            +

error[E0506]: cannot assign to `current.next` because it is borrowed
  --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:86:17
   |
71 |                     match &mut current.next {
   |                           ----------------- `current.next` is borrowed here
...
86 |                 current.next = Some(Box::new(new_entry));
   |                 ^^^^^^^^^^^^
   |                 |
   |                 `current.next` is assigned to here but it was already borrowed
   |                 borrow later used here

error[E0506]: cannot assign to `current.next` because it is borrowed
   --> /var/folders/hd/dlntwdzx0rg8g8z7x52tdkp80000gn/T/tmpcg0kgcfd/lib.rs:138:29
    |
136 |                     while let Some(next_entry) = &mut current.next {
    |                                                  ----------------- `current.next` is borrowed here
137 |                         if next_entry.key == *key {
138 |                             current.next = next_entry.next.take();
    |                             ^^^^^^^^^^^^
    |                             |
    |                             `current.next` is assigned to here but it was already borrowed
    |                             borrow later used here

error: aborting due to 3 previous errors; 1 warning emitted

Some errors have detailed explanations: E0277, E0506.
For more information about an error, try `rustc --explain E0277`.

🔍 Failure Analysis

Error Type: E0506: Borrow Checker Violation

Summary: Cannot assign to a variable because it is borrowed

Location: Line 5: `use std::ptr;`

What Went Wrong

Rust's borrow checker prevents you from modifying a variable while you have an active borrow of it. In this case, the code tries to assign to a mutable reference (`*current`) while `entry` still borrows it. This is a classic pattern when working with self-referential data structures like linked lists.

Root Cause

The LLM translated C's pointer manipulation patterns directly to Rust, but didn't account for Rust's ownership rules. C allows you to modify pointers freely, but Rust tracks borrows statically and prevents simultaneous aliasing + mutation.

How to Fix It

Use `std::mem::replace()` or restructure the code to avoid borrowing and mutating simultaneously:
```rust
// Instead of:
*current = entry.next.take();

// Use:
let next = std::mem::replace(current, None);
*current = next.and_then(|mut e| e.next.take());
```
Or use `Option::take()` earlier to move ownership before the borrow.

Generated Rust Code (4309 bytes)

use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr;

const HASHMAP_INITIAL_CAPACITY: usize = 16;

#[derive(Debug)]
struct HashMapError(String);

impl fmt::Display for HashMapError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HashMapError: {}", self.0)
    }
}

impl Error for HashMapError {}

type Result<T> = std::result::Result<T, HashMapError>;

#[derive(Debug)]
struct HashMapEntry<K, V> {
    key: K,
    value: V,
    next: Option<Box<HashMapEntry<K, V>>>,
}

#[derive(Debug)]
struct HashMap<K, V> {
    buckets: Vec<Option<Box<HashMapEntry<K, V>>>>,
    capacity: usize,
    size: usize,
}

impl<K, V> HashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    /// Creates a new hash map with the initial capacity.
    fn new() -> Self {
        let capacity = HASHMAP_INITIAL_CAPACITY;
        let buckets = vec![None; capacity];
        HashMap {
            buckets,
            capacity,
            size: 0,
        }
    }

    /// Generates a hash value for the given key.
    fn hash_string(key: &K) -> u64 {
        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        hasher.finish()
    }

    /// Inserts a key-value pair into the hash map.
    fn put(&mut self, key: K, value: V) -> Result<()> {
        let hash = Self::hash_string(&key);
        let index = (hash % self.capacity as u64) as usize;

        match &mut self.buckets[index] {
            Some(entry) => {
                let mut current = entry;
                loop {
                    if current.key == key {
                        current.value = value;
                        return Ok(());
                    }
                    match &mut current.next {
                        Some(next_entry) => {
                            current = next_entry;
                        }
                        None => {
                            break;
                        }
                    }
                }

                let new_entry = HashMapEntry {
                    key: key.clone(),
                    value,
                    next: None,
                };
                current.next = Some(Box::new(new_entry));
            }
            None => {
                let new_entry = HashMapEntry {
                    key: key.clone(),
                    value,
                    next: None,
                };
                self.buckets[index] = Some(Box::new(new_entry));
            }
        }

        self.size += 1;
        Ok(())
    }

    /// Retrieves the value associated with the given key.
    fn get(&self, key: &K) -> Option<&V> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = self.buckets[index].as_ref()?;
        loop {
            if current.key == *key {
                return Some(&current.value);
            }
            match &current.next {
                Some(next_entry) => {
                    current = next_entry;
                }
                None => {
                    return None;
                }
            }
        }
    }

    /// Removes a key-value pair from the hash map.
    fn remove(&mut self, key: &K) -> Result<()> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        match &mut self.buckets[index] {
            Some(entry) => {
                if entry.key == *key {
                    self.buckets[index] = entry.next.take();
                    self.size -= 1;
                    return Ok(());
                } else {
                    let mut current = entry;
                    while let Some(next_entry) = &mut current.next {
                        if next_entry.key == *key {
                            current.next = next_entry.next.take();
                            self.size -= 1;
                            return Ok(());
                        }
                        current = next_entry;
                    }
                }
            }
            None => {
                return Err(HashMapError("Key not found".to_string()));
            }
        }

        Err(HashMapError("Key not found".to_string()))
    }
}

Model Comparison

Model Total Compilation Safety Quality Correctness Maintainability Performance
CLAUDE 53/100 0/25 20/20 15/20 3/15 5/10 10/10
GEMINI 52/100 0/25 20/20 17/20 3/15 4/10 8/10

Evaluation Methodology

CELLO evaluates transpilation quality across 6 dimensions:

Total possible score: 100 points