use std::alloc::{alloc, dealloc, Layout};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::ptr::{self, NonNull};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AllocError;
impl fmt::Display for AllocError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "heap allocation failed (out of memory)")
}
}
impl std::error::Error for AllocError {}
#[doc(alias = "heap")]
#[doc(alias = "box")]
#[doc(alias = "pointer")]
pub struct MyBox<T> {
ptr: NonNull<T>,
}
unsafe impl<T: Send> Send for MyBox<T> {}
unsafe impl<T: Sync> Sync for MyBox<T> {}
impl<T> MyBox<T> {
pub fn new(value: T) -> Self {
let layout = Layout::new::<T>();
if layout.size() == 0 {
return Self {
ptr: NonNull::dangling(),
};
}
unsafe {
let raw_ptr = alloc(layout) as *mut T;
let non_null_ptr = NonNull::new(raw_ptr)
.expect("Fatal: out of memory on the heap");
ptr::write(non_null_ptr.as_ptr(), value);
Self { ptr: non_null_ptr }
}
}
pub fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
pub fn into_raw(self) -> *mut T {
let raw = self.ptr.as_ptr();
std::mem::forget(self);
raw
}
pub unsafe fn from_raw(raw: *mut T) -> Self {
Self {
ptr: NonNull::new_unchecked(raw),
}
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
let layout = Layout::new::<T>();
unsafe {
ptr::drop_in_place(self.ptr.as_ptr());
if layout.size() > 0 {
dealloc(self.ptr.as_ptr() as *mut u8, layout);
}
}
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref() }
}
}
impl<T> DerefMut for MyBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.as_mut() }
}
}
impl<T: Clone> Clone for MyBox<T> {
fn clone(&self) -> Self {
let layout = Layout::new::<T>();
if layout.size() == 0 {
return Self {
ptr: NonNull::dangling(),
};
}
unsafe {
let new_raw = alloc(layout) as *mut u8;
let new_non_null = NonNull::new(new_raw)
.expect("Fatal: out of memory on the heap (clone)");
let dest = new_non_null.as_ptr() as *mut T;
ptr::write(dest, (**self).clone());
Self { ptr: NonNull::new_unchecked(dest) }
}
}
}
impl<T: Default> Default for MyBox<T> {
fn default() -> Self {
MyBox::new(T::default())
}
}
impl<T> From<T> for MyBox<T> {
fn from(t: T) -> Self {
MyBox::new(t)
}
}
impl<T: PartialEq> PartialEq for MyBox<T> {
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(&**self, &**other)
}
fn ne(&self, other: &Self) -> bool {
PartialEq::ne(&**self, &**other)
}
}
impl<T: Eq> Eq for MyBox<T> {}
impl<T: PartialOrd> PartialOrd for MyBox<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
fn lt(&self, other: &Self) -> bool {
PartialOrd::lt(&**self, &**other)
}
fn le(&self, other: &Self) -> bool {
PartialOrd::le(&**self, &**other)
}
fn gt(&self, other: &Self) -> bool {
PartialOrd::gt(&**self, &**other)
}
fn ge(&self, other: &Self) -> bool {
PartialOrd::ge(&**self, &**other)
}
}
impl<T: Ord> Ord for MyBox<T> {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: Hash> Hash for MyBox<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: fmt::Debug> fmt::Debug for MyBox<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: fmt::Display> fmt::Display for MyBox<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T> AsRef<T> for MyBox<T> {
fn as_ref(&self) -> &T {
&**self
}
}
impl<T> AsMut<T> for MyBox<T> {
fn as_mut(&mut self) -> &mut T {
&mut **self
}
}
impl<T> std::borrow::Borrow<T> for MyBox<T> {
fn borrow(&self) -> &T {
&**self
}
}
impl<T> std::borrow::BorrowMut<T> for MyBox<T> {
fn borrow_mut(&mut self) -> &mut T {
&mut **self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_allocation_and_deref() {
let num = MyBox::new(42);
assert_eq!(*num, 42);
}
#[test]
fn mutation_through_deref() {
let mut num = MyBox::new(10);
*num += 20;
assert_eq!(*num, 30);
}
#[test]
fn zero_sized_type_allocation() {
let zst = MyBox::new(());
assert_eq!(*zst, ());
}
#[test]
fn struct_field_access_via_autoderef() {
#[derive(Debug, Clone, PartialEq)]
struct Character {
name: String,
hp: u32,
}
let hero = MyBox::new(Character {
name: String::from("Aldric"),
hp: 100,
});
assert_eq!(hero.name, "Aldric");
assert_eq!(hero.hp, 100);
}
#[test]
fn method_call_via_autoderef() {
#[derive(Debug, PartialEq)]
struct TaskList(Vec<i32>);
impl TaskList {
fn sort(&mut self) {
self.0.sort();
}
}
let mut tasks = MyBox::new(TaskList(vec![3, 1, 4, 1, 5]));
tasks.sort();
assert_eq!(tasks.0, vec![1, 1, 3, 4, 5]);
}
#[test]
fn deep_clone_produces_independent_copy() {
let original = MyBox::new(vec![1, 2, 3]);
let mut cloned = original.clone();
assert_eq!(*original, *cloned);
cloned.push(4);
assert_eq!(*original, vec![1, 2, 3]);
assert_eq!(*cloned, vec![1, 2, 3, 4]);
}
#[test]
fn drop_frees_memory() {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
let drop_count = Arc::new(AtomicUsize::new(0));
struct DropCounter(Arc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
{
let counter = MyBox::new(DropCounter(drop_count.clone()));
assert_eq!(drop_count.load(Ordering::SeqCst), 0);
drop(counter);
}
assert_eq!(drop_count.load(Ordering::SeqCst), 1);
}
#[test]
fn partial_eq_and_ord() {
let a = MyBox::new(5);
let b = MyBox::new(10);
let c = MyBox::new(5);
assert_eq!(a, c);
assert_ne!(a, b);
assert!(a < b);
assert!(b > a);
assert!(a <= c);
assert!(a >= c);
}
#[test]
fn display_formatting() {
let s = MyBox::new(String::from("hello"));
assert_eq!(format!("{}", s), "hello");
}
#[test]
fn debug_formatting() {
let n = MyBox::new(42);
assert_eq!(format!("{:?}", n), "42");
}
#[test]
fn as_ref_and_as_mut() {
let mut boxed = MyBox::new(vec![10, 20, 30]);
let slice: &[i32] = boxed.as_ref();
assert_eq!(slice, &[10, 20, 30]);
boxed.as_mut().push(40);
assert_eq!(boxed.as_ref(), &[10, 20, 30, 40]);
}
#[test]
fn into_raw_and_from_raw_roundtrip() {
let original = MyBox::new(128);
let raw = original.into_raw();
let reconstructed = unsafe { MyBox::from_raw(raw) };
assert_eq!(*reconstructed, 128);
}
#[test]
fn box_in_vec_moves_without_copying_data() {
let v1 = MyBox::new(String::from("alpha"));
let v2 = MyBox::new(String::from("beta"));
let mut vec = vec![v1, v2];
vec.sort_by(|a, b| b.as_str().cmp(a.as_str()));
assert_eq!(vec[0].as_str(), "beta");
assert_eq!(vec[1].as_str(), "alpha");
}
#[test]
fn recursive_structure_with_box() {
#[derive(Debug, PartialEq)]
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
let list: List<i32> =
List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
assert_eq!(
list,
List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))))
);
}
#[test]
fn hash_trait_works() {
use std::collections::hash_map::DefaultHasher;
let a = MyBox::new(7);
let b = MyBox::new(7);
let c = MyBox::new(9);
let mut hasher_a = DefaultHasher::new();
a.hash(&mut hasher_a);
let mut hasher_b = DefaultHasher::new();
b.hash(&mut hasher_b);
let mut hasher_c = DefaultHasher::new();
c.hash(&mut hasher_c);
assert_eq!(hasher_a.finish(), hasher_b.finish());
assert_ne!(hasher_a.finish(), hasher_c.finish());
}
#[test]
fn default_trait() {
let v: MyBox<Vec<i32>> = MyBox::default();
assert!(v.is_empty());
}
#[test]
fn from_trait_constructor() {
let s: MyBox<String> = String::from("from trait").into();
assert_eq!(*s, "from trait");
}
}