Skip to content

Implement standard Rust traits for better ergonomics #28

@RAprogramm

Description

@RAprogramm

Implement standard Rust traits to improve API ergonomics and Rust ecosystem integration.

Objectives

  • Implement Clone, PartialEq, Eq, Hash traits
  • Add FromIterator and IntoIterator
  • Implement Index and IndexMut
  • Add AsRef and Deref traits
  • Improve API usability

Traits to Implement

1. Clone

impl Clone for CStringArray {
    fn clone(&self) -> Self {
        Self::from_cstrings(self.strings.clone())
            .expect("clone from non-empty array")
    }
}

Use cases:

let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
let arr2 = arr1.clone();

2. PartialEq + Eq

impl PartialEq for CStringArray {
    fn eq(&self, other: &Self) -> bool {
        self.strings == other.strings
    }
}

impl Eq for CStringArray {}

Use cases:

let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
let arr2 = CStringArray::new(vec!["a".to_string()]).unwrap();
assert_eq!(arr1, arr2);

3. Hash

impl Hash for CStringArray {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.strings.hash(state);
    }
}

Use cases:

let mut map = HashMap::new();
let arr = CStringArray::new(vec!["key".to_string()]).unwrap();
map.insert(arr, "value");

4. FromIterator

impl FromIterator<String> for CStringArray {
    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
        let strings: Vec<String> = iter.collect();
        Self::new(strings).expect("FromIterator from non-empty iterator")
    }
}

impl FromIterator<CString> for CStringArray {
    fn from_iter<I: IntoIterator<Item = CString>>(iter: I) -> Self {
        let strings: Vec<CString> = iter.collect();
        Self::from_cstrings(strings).expect("FromIterator from non-empty iterator")
    }
}

Use cases:

let arr: CStringArray = vec!["a", "b", "c"]
    .into_iter()
    .map(String::from)
    .collect();

5. IntoIterator

impl IntoIterator for CStringArray {
    type Item = CString;
    type IntoIter = std::vec::IntoIter<CString>;

    fn into_iter(self) -> Self::IntoIter {
        self.strings.into_iter()
    }
}

impl<'a> IntoIterator for &'a CStringArray {
    type Item = &'a CString;
    type IntoIter = std::slice::Iter<'a, CString>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

Use cases:

let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
for s in &arr {
    println!("{}", s.to_str().unwrap());
}
for s in arr {
    println!("{}", s.to_str().unwrap());
}

6. Index

impl Index<usize> for CStringArray {
    type Output = CString;

    fn index(&self, index: usize) -> &Self::Output {
        &self.strings[index]
    }
}

Use cases:

let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
let first = &arr[0];

7. AsRef

impl AsRef<[CString]> for CStringArray {
    fn as_ref(&self) -> &[CString] {
        &self.strings
    }
}

Use cases:

fn process_strings(strings: &[CString]) { }

let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
process_strings(arr.as_ref());

8. Default (optional)

Note: Current API doesn't allow empty arrays. Could add a separate EmptyCStringArray type or relax constraint.

Implementation Plan

1. Add trait implementations to src/traits.rs

Create organized sections:

// Comparison traits
impl PartialEq for CStringArray { }
impl Eq for CStringArray {}
impl Hash for CStringArray { }

// Clone trait
impl Clone for CStringArray { }

// Iterator traits
impl FromIterator<String> for CStringArray { }
impl FromIterator<CString> for CStringArray { }
impl IntoIterator for CStringArray { }
impl<'a> IntoIterator for &'a CStringArray { }

// Indexing traits
impl Index<usize> for CStringArray { }

// Conversion traits
impl AsRef<[CString]> for CStringArray { }

2. Add comprehensive tests

#[test]
fn test_clone() {
    let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
    let arr2 = arr1.clone();
    assert_eq!(arr1, arr2);
    assert_ne!(arr1.as_ptr(), arr2.as_ptr());
}

#[test]
fn test_equality() {
    let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
    let arr2 = CStringArray::new(vec!["a".to_string()]).unwrap();
    let arr3 = CStringArray::new(vec!["b".to_string()]).unwrap();
    
    assert_eq!(arr1, arr2);
    assert_ne!(arr1, arr3);
}

#[test]
fn test_hash() {
    let mut map = HashMap::new();
    let arr = CStringArray::new(vec!["key".to_string()]).unwrap();
    map.insert(arr.clone(), "value");
    assert_eq!(map.get(&arr), Some(&"value"));
}

#[test]
fn test_from_iterator() {
    let arr: CStringArray = vec!["a", "b", "c"]
        .into_iter()
        .map(String::from)
        .collect();
    assert_eq!(arr.len(), 3);
}

#[test]
fn test_into_iterator() {
    let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
    let collected: Vec<_> = arr.into_iter().collect();
    assert_eq!(collected.len(), 1);
}

#[test]
fn test_index() {
    let arr = CStringArray::new(vec!["a".to_string(), "b".to_string()]).unwrap();
    assert_eq!(arr[0].to_str().unwrap(), "a");
    assert_eq!(arr[1].to_str().unwrap(), "b");
}

#[test]
#[should_panic]
fn test_index_out_of_bounds() {
    let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
    let _ = &arr[10];
}

#[test]
fn test_as_ref() {
    let arr = CStringArray::new(vec!["a".to_string()]).unwrap();
    let slice: &[CString] = arr.as_ref();
    assert_eq!(slice.len(), 1);
}

3. Update documentation

Add examples to lib.rs:

/// # Trait Implementations
///
/// ## Cloning
/// ```
/// let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
/// let arr2 = arr1.clone();
/// ```
///
/// ## Equality
/// ```
/// let arr1 = CStringArray::new(vec!["a".to_string()]).unwrap();
/// let arr2 = CStringArray::new(vec!["a".to_string()]).unwrap();
/// assert_eq!(arr1, arr2);
/// ```

Benefits

  • Ergonomics: More idiomatic Rust API
  • Integration: Works with standard library collections
  • Usability: Indexing with [] instead of get()
  • Iteration: for loops without calling iter()
  • Comparison: Can use in HashMaps, BTreeSets
  • Professional: Follows Rust API guidelines

Breaking Changes

None - all additions are backward compatible.

Success Criteria

  • All 8 trait implementations completed
  • Comprehensive test coverage
  • Documentation with examples
  • No clippy warnings
  • All existing tests pass
  • REUSE 3.3 compliant

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions