Test post
Wed Jun 17, 2020
use std::ptr::null_mut;

struct AllenbongStrings<const SIZE: usize> {
    // N.b. it doesn't actually make sense to store pointers to Strings in
    // Rust because Strings (as opposed to &str) internally store a pointer
    // to heap allocated data.
    strings: [*mut String; SIZE],
    index: usize,
}

impl<const SIZE: usize> AllenbongStrings<SIZE> {
    pub fn new() -> Self {
        // Fill up the array with null pointers.
        // This is not something we'd do in Rust either.
        let strings = [null_mut::<String>(); SIZE];

        Self { strings, index: 0 }
    }

    pub fn add_string<S>(&mut self, item: S)
    where
        S: Into<String>,
    {
        if self.index < SIZE {
            // Convert item into a Box<String>...
            let item_boxed = Box::new(item.into());
            // And then produce a *mut String. This tells Rust we intend to manage a raw pointer.
            let item_ptr = Box::into_raw(item_boxed);
            // Insert the pointer at a valid location in the array.
            self.strings[self.index] = item_ptr;
            // Increment the index.
            self.index += 1;
        } else {
            eprintln!("You can't add anymore Strings, idiot. Learn to count.");
        }
    }

    pub fn iter_mut<'iter>(&'iter mut self) -> AllenIter<'iter, SIZE> {
        AllenIter::new(self)
    }
}

impl<const SIZE: usize> Drop for AllenbongStrings<SIZE> {
    fn drop(&mut self) {
        for str_ptr in self.iter_mut() {
            // String is dropped and deallocated at the end of this block
            unsafe {
                // Convert the raw pointer back into a Box
                let die_lol = Box::from_raw(str_ptr);
                println!("Dropping {}", die_lol);
                //*str_ptr = std::ptr::null_mut();
            }
        }
        println!("Strings were deleted without a double free.");
    }
}

// An iterator so we don't have to manage indices.
// The 'iter thing is a lifetime so AllenIter can't die before allenbong.
struct AllenIter<'iter, const SIZE: usize> {
    // Borrowing (i.e. a reference) to AllenbongStrings
    allenbong: &'iter AllenbongStrings<SIZE>,
    // Index for the iterator which will always be less than allenbong.index
    index: usize
}

impl<'iter, const SIZE: usize> AllenIter<'iter, SIZE> {
    fn new(allenbong: &'iter mut AllenbongStrings<SIZE>) -> Self {
        Self {
            allenbong,
            index: 0
        }
    }
}

// Implementing Iterator so I don't have to mess with an index.
impl<'iter, const SIZE: usize> Iterator for AllenIter<'iter, SIZE> {
    // Pointers are Copy so I can just return pointers.
    type Item = *mut String;

    fn next(&mut self) -> Option<Self::Item> {
        let current_index = self.index;

        // As long as index < allenbong.index then the data can be accessed
        // safely (i.e. no dangling pointers).
        if current_index < self.allenbong.index {
            self.index += 1;
            Some(self.allenbong.strings[current_index])
        }
        else {
            None
        }
    }
}

fn main() {
    // This allocates an array of 50 in the object but...
    let mut allen = AllenbongStrings::<50>::new();
    // We're only adding 1..2...5! 5! Strings.
    allen.add_string("Meow");
    allen.add_string("Woof");
    allen.add_string("Moo");
    allen.add_string("delete bert pls");
    allen.add_string("Hi kebinlol");

    // allen dies here. I mean the object.
    // But the other 45 null pointers aren't referenced. Success!
    // Also no double frees/segmentation faults.
}

about · data science and python · rust · home