diff --git a/crates/lune-std-ffi/src/c/c_arr.rs b/crates/lune-std-ffi/src/c/c_arr.rs
index 2b2b94b..2a42019 100644
--- a/crates/lune-std-ffi/src/c/c_arr.rs
+++ b/crates/lune-std-ffi/src/c/c_arr.rs
@@ -1,3 +1,5 @@
+use std::any::Any;
+
 use libffi::middle::Type;
 use mlua::prelude::*;
 
@@ -54,13 +56,13 @@ impl CArr {
         Ok(carr)
     }
 
-    pub fn get_type(&self) -> Type {
-        self.struct_type.clone()
+    pub fn get_type(&self) -> &Type {
+        &self.struct_type
     }
 
-    // pub fn get_element_type(&self) -> Type {
-    //     self.element_type.clone()
-    // }
+    pub fn get_element_type(&self) -> &Type {
+        &self.element_type
+    }
 
     // Stringify cstruct for pretty printing something like:
     // <CStruct( u8, i32, size = 8 )>
@@ -73,7 +75,7 @@ impl CArr {
                 .as_userdata()
                 .ok_or(LuaError::external("failed to get inner type userdata."))?;
 
-            if inner.is::<CType>() {
+            if inner.is::<CType<dyn Any>>() {
                 Ok(format!(
                     " {} ; {} ",
                     stringify_userdata(inner)?,
diff --git a/crates/lune-std-ffi/src/c/c_helper.rs b/crates/lune-std-ffi/src/c/c_helper.rs
index d25843c..2a18096 100644
--- a/crates/lune-std-ffi/src/c/c_helper.rs
+++ b/crates/lune-std-ffi/src/c/c_helper.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
 use std::ptr::{self, null_mut};
 
 use libffi::{low, middle::Type, raw};
@@ -37,11 +38,11 @@ pub fn type_list_from_table(table: &LuaTable) -> LuaResult<Vec<Type>> {
 // get libffi_type from any c-type userdata
 pub fn type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult<Type> {
     if userdata.is::<CStruct>() {
-        Ok(userdata.borrow::<CStruct>()?.get_type())
-    } else if userdata.is::<CType>() {
-        Ok(userdata.borrow::<CType>()?.get_type())
+        Ok(userdata.borrow::<CStruct>()?.get_type().to_owned())
+    } else if userdata.is::<CType<dyn Any>>() {
+        Ok(userdata.borrow::<CType<dyn Any>>()?.get_type().to_owned())
     } else if userdata.is::<CArr>() {
-        Ok(userdata.borrow::<CArr>()?.get_type())
+        Ok(userdata.borrow::<CArr>()?.get_type().to_owned())
     } else if userdata.is::<CPtr>() {
         Ok(CPtr::get_type())
     } else {
@@ -59,9 +60,10 @@ pub fn type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult<Type> {
 
 // stringify any c-type userdata (for recursive)
 pub fn stringify_userdata(userdata: &LuaAnyUserData) -> LuaResult<String> {
-    if userdata.is::<CType>() {
-        let name = userdata.borrow::<CType>()?.stringify();
-        Ok(name)
+    if userdata.is::<CType<dyn Any>>() {
+        Ok(String::from(
+            userdata.borrow::<CType<dyn Any>>()?.stringify(),
+        ))
     } else if userdata.is::<CStruct>() {
         let name = CStruct::stringify(userdata)?;
         Ok(name)
@@ -80,7 +82,7 @@ pub fn stringify_userdata(userdata: &LuaAnyUserData) -> LuaResult<String> {
 pub fn name_from_userdata(userdata: &LuaAnyUserData) -> String {
     if userdata.is::<CStruct>() {
         String::from("CStruct")
-    } else if userdata.is::<CType>() {
+    } else if userdata.is::<CType<dyn Any>>() {
         String::from("CType")
     } else if userdata.is::<CArr>() {
         String::from("CArr")
diff --git a/crates/lune-std-ffi/src/c/c_struct.rs b/crates/lune-std-ffi/src/c/c_struct.rs
index 58628d5..8812eb5 100644
--- a/crates/lune-std-ffi/src/c/c_struct.rs
+++ b/crates/lune-std-ffi/src/c/c_struct.rs
@@ -1,12 +1,9 @@
 #![allow(clippy::cargo_common_metadata)]
 
-use std::{borrow::Borrow, vec::Vec};
+use std::any::Any;
+use std::vec::Vec;
 
-use libffi::{
-    low,
-    middle::{Cif, Type},
-    raw,
-};
+use libffi::{low, middle::Type, raw};
 use mlua::prelude::*;
 
 use super::association_names::CSTRUCT_INNER;
@@ -20,14 +17,14 @@ use crate::ffi::ffi_helper::FFI_STATUS_NAMES;
 pub struct CStruct {
     // libffi_cif: Cif,
     fields: Vec<Type>,
-    libffi_type: Type,
+    struct_type: Type,
     offsets: Vec<usize>,
     size: usize,
 }
 
 impl CStruct {
     pub fn new(fields: Vec<Type>) -> LuaResult<Self> {
-        let libffi_type = Type::structure(fields.iter().cloned());
+        let struct_type = Type::structure(fields.iter().cloned());
         // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
 
         // Get field offsets with ffi_get_struct_offsets
@@ -35,7 +32,7 @@ impl CStruct {
         unsafe {
             let offset_result: raw::ffi_status = raw::ffi_get_struct_offsets(
                 low::ffi_abi_FFI_DEFAULT_ABI,
-                libffi_type.as_raw_ptr(),
+                struct_type.as_raw_ptr(),
                 offsets.as_mut_ptr(),
             );
             if offset_result != raw::ffi_status_FFI_OK {
@@ -49,12 +46,12 @@ impl CStruct {
 
         // Get tailing padded size of struct
         // See http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html
-        let size = unsafe { (*libffi_type.as_raw_ptr()).size };
+        let size = unsafe { (*struct_type.as_raw_ptr()).size };
 
         Ok(Self {
             // libffi_cif: libffi_cfi,
             fields,
-            libffi_type,
+            struct_type,
             offsets,
             size,
         })
@@ -85,7 +82,7 @@ impl CStruct {
             let mut result = String::from(" ");
             for i in 0..table.raw_len() {
                 let child: LuaAnyUserData = table.raw_get(i + 1)?;
-                if child.is::<CType>() {
+                if child.is::<CType<dyn Any>>() {
                     result.push_str(format!("{}, ", stringify_userdata(&child)?).as_str());
                 } else {
                     result.push_str(
@@ -117,8 +114,12 @@ impl CStruct {
         Ok(offset)
     }
 
-    pub fn get_type(&self) -> Type {
-        self.libffi_type.clone()
+    pub fn get_fields(&self) -> &Vec<Type> {
+        &self.fields
+    }
+
+    pub fn get_type(&self) -> &Type {
+        &self.struct_type
     }
 }
 
diff --git a/crates/lune-std-ffi/src/c/c_type.rs b/crates/lune-std-ffi/src/c/c_type.rs
index 7ed7bca..3e4ee42 100644
--- a/crates/lune-std-ffi/src/c/c_type.rs
+++ b/crates/lune-std-ffi/src/c/c_type.rs
@@ -1,5 +1,7 @@
 #![allow(clippy::cargo_common_metadata)]
 
+use std::marker::PhantomData;
+
 use libffi::middle::Type;
 use mlua::prelude::*;
 
@@ -8,27 +10,20 @@ use super::c_helper::get_ensured_size;
 use super::c_ptr::CPtr;
 use crate::ffi::ffi_helper::get_ptr_from_userdata;
 
-pub struct CType {
+pub struct CType<T: ?Sized> {
     // for ffi_ptrarray_to_raw?
     // libffi_cif: Cif,
     libffi_type: Type,
     size: usize,
-    name: Option<String>,
-
-    // Write converted data from luavalue into some ptr
-    pub luavalue_into_ptr: fn(value: LuaValue, ptr: *mut ()) -> LuaResult<()>,
-
-    // Read luavalue from some ptr
-    pub ptr_into_luavalue: fn(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue>,
+    name: Option<&'static str>,
+    _phantom: PhantomData<T>,
 }
 
-impl CType {
-    pub fn new(
-        libffi_type: Type,
-        name: Option<String>,
-        luavalue_into_ptr: fn(value: LuaValue, ptr: *mut ()) -> LuaResult<()>,
-        ptr_into_luavalue: fn(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue>,
-    ) -> LuaResult<Self> {
+impl<T> CType<T>
+where
+    T: ?Sized,
+{
+    pub fn new_with_libffi_type(libffi_type: Type, name: Option<&'static str>) -> LuaResult<Self> {
         // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
         let size = get_ensured_size(libffi_type.as_raw_ptr())?;
         Ok(Self {
@@ -36,78 +31,83 @@ impl CType {
             libffi_type,
             size,
             name,
-            luavalue_into_ptr,
-            ptr_into_luavalue,
+            _phantom: PhantomData {},
         })
     }
 
-    pub fn get_type(&self) -> Type {
-        self.libffi_type.clone()
+    pub fn get_type(&self) -> &Type {
+        &self.libffi_type
     }
 
-    pub fn stringify(&self) -> String {
-        match &self.name {
-            Some(t) => t.to_owned(),
-            None => String::from("unnamed"),
+    pub fn stringify(&self) -> &str {
+        match self.name {
+            Some(t) => t,
+            None => "unnamed",
         }
     }
+}
 
-    // Read data from ptr and convert it into luavalue
-    pub unsafe fn read_ptr<'lua>(
+pub trait PtrHandle {
+    // Convert luavalue into data, then write into ptr
+    fn luavalue_into_ptr(value: LuaValue, ptr: *mut ()) -> LuaResult<()>;
+
+    // Read data from ptr, then convert into luavalue
+    fn ptr_into_luavalue(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue>;
+
+    // Read data from userdata (such as box or ref) and convert it into luavalue
+    unsafe fn read_userdata<'lua>(
         &self,
         lua: &'lua Lua,
         userdata: LuaAnyUserData<'lua>,
         offset: Option<isize>,
     ) -> LuaResult<LuaValue<'lua>> {
         let ptr = unsafe { get_ptr_from_userdata(&userdata, offset)? };
-        let value = (self.ptr_into_luavalue)(lua, ptr)?;
+        let value = Self::ptr_into_luavalue(lua, ptr)?;
         Ok(value)
     }
 
-    // Write converted data from luavalue into ptr
-    pub unsafe fn write_ptr<'lua>(
+    // Write data into userdata (such as box or ref) from luavalue
+    unsafe fn write_userdata<'lua>(
         &self,
         luavalue: LuaValue<'lua>,
         userdata: LuaAnyUserData<'lua>,
         offset: Option<isize>,
     ) -> LuaResult<()> {
         let ptr = unsafe { get_ptr_from_userdata(&userdata, offset)? };
-        (self.luavalue_into_ptr)(luavalue, ptr)?;
+        Self::luavalue_into_ptr(luavalue, ptr)?;
         Ok(())
     }
 }
 
-impl LuaUserData for CType {
+impl<T> LuaUserData for CType<T>
+where
+    Self: Sized + PtrHandle,
+{
     fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
         fields.add_field_method_get("size", |_, this| Ok(this.size));
     }
 
     fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
         methods.add_function("ptr", |lua, this: LuaAnyUserData| {
-            let pointer = CPtr::from_lua_userdata(lua, &this)?;
-            Ok(pointer)
+            CPtr::from_lua_userdata(lua, &this)
         });
         methods.add_method(
             "from",
-            |lua, ctype, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
-                let value = unsafe { ctype.read_ptr(lua, userdata, offset)? };
-                Ok(value)
+            |lua, ctype, (userdata, offset): (LuaAnyUserData, Option<isize>)| unsafe {
+                ctype.read_userdata(lua, userdata, offset)
             },
         );
         methods.add_method(
             "into",
-            |_, ctype, (value, userdata, offset): (LuaValue, LuaAnyUserData, Option<isize>)| {
-                unsafe { ctype.write_ptr(value, userdata, offset)? };
-                Ok(())
+            |_, ctype, (value, userdata, offset): (LuaValue, LuaAnyUserData, Option<isize>)| unsafe {
+                ctype.write_userdata(value, userdata, offset)
             },
         );
         methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
-            let carr = CArr::from_lua_userdata(lua, &this, length)?;
-            Ok(carr)
+            CArr::from_lua_userdata(lua, &this, length)
         });
-        methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
-            let name = this.stringify();
-            Ok(name)
+        methods.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| {
+            lua.create_string(this.stringify())
         });
     }
 }
diff --git a/crates/lune-std-ffi/src/c/mod.rs b/crates/lune-std-ffi/src/c/mod.rs
index 25db176..be3364d 100644
--- a/crates/lune-std-ffi/src/c/mod.rs
+++ b/crates/lune-std-ffi/src/c/mod.rs
@@ -5,6 +5,9 @@ pub(super) mod c_ptr;
 pub(super) mod c_string;
 pub(super) mod c_struct;
 pub(super) mod c_type;
+pub(super) mod types;
+
+pub use types::create_all_types;
 
 // Named registry table names
 mod association_names {
@@ -12,133 +15,3 @@ mod association_names {
     pub const CARR_INNER: &str = "__carr_inner";
     pub const CSTRUCT_INNER: &str = "__cstruct_inner";
 }
-
-use core::ffi::{
-    c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
-    c_ulong, c_ulonglong, c_ushort, c_void,
-};
-
-use libffi::middle::Type;
-use mlua::prelude::*;
-
-use self::c_type::CType;
-use crate::ffi::ffi_platform::CHAR_IS_SIGNED;
-
-// export all default c-types
-#[allow(clippy::too_many_lines)]
-pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
-    Ok(vec![
-        (
-            "int",
-            CType::new(
-                Type::c_int(),
-                Some(String::from("int")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_int;
-                    unsafe {
-                        *(ptr.cast::<c_int>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut ()| {
-                    let value = unsafe { (*ptr.cast::<c_int>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "long",
-            CType::new(
-                Type::c_long(),
-                Some(String::from("long")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_long;
-                    unsafe {
-                        *(ptr.cast::<c_long>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut ()| {
-                    let value = unsafe { (*ptr.cast::<c_long>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "longlong",
-            CType::new(
-                Type::c_longlong(),
-                Some(String::from("longlong")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_longlong;
-                    unsafe {
-                        *(ptr.cast::<c_longlong>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut ()| {
-                    let value = unsafe { (*ptr.cast::<c_longlong>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "char",
-            CType::new(
-                if CHAR_IS_SIGNED {
-                    Type::c_schar()
-                } else {
-                    Type::c_uchar()
-                },
-                Some(String::from("char")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_char;
-                    unsafe {
-                        *(ptr.cast::<c_char>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut ()| {
-                    let value = unsafe { (*ptr.cast::<c_char>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-    ])
-}
diff --git a/crates/lune-std-ffi/src/c/types/c_int.rs b/crates/lune-std-ffi/src/c/types/c_int.rs
new file mode 100644
index 0000000..3e36a69
--- /dev/null
+++ b/crates/lune-std-ffi/src/c/types/c_int.rs
@@ -0,0 +1,38 @@
+use core::ffi::c_int;
+
+use libffi::middle::Type;
+use mlua::prelude::*;
+
+use super::super::c_type::{CType, PtrHandle};
+
+impl PtrHandle for CType<c_int> {
+    fn luavalue_into_ptr(value: LuaValue, ptr: *mut ()) -> LuaResult<()> {
+        let value = match value {
+            LuaValue::Integer(t) => t,
+            _ => {
+                return Err(LuaError::external(format!(
+                    "Integer expected, got {}",
+                    value.type_name()
+                )))
+            }
+        } as c_int;
+        unsafe {
+            *(ptr.cast::<c_int>()) = value;
+        }
+        Ok(())
+    }
+    fn ptr_into_luavalue(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue> {
+        let value = unsafe { (*ptr.cast::<c_int>()).into_lua(lua)? };
+        Ok(value)
+    }
+}
+
+impl CType<c_int> {
+    fn new() -> LuaResult<Self> {
+        Self::new_with_libffi_type(Type::c_int(), Some("int"))
+    }
+}
+
+pub fn get_export(lua: &Lua) -> LuaResult<(&'static str, LuaAnyUserData)> {
+    Ok(("int", lua.create_userdata(CType::<c_int>::new()?)?))
+}
diff --git a/crates/lune-std-ffi/src/c/types/mod.rs b/crates/lune-std-ffi/src/c/types/mod.rs
new file mode 100644
index 0000000..0d4986d
--- /dev/null
+++ b/crates/lune-std-ffi/src/c/types/mod.rs
@@ -0,0 +1,8 @@
+mod c_int;
+
+use mlua::prelude::*;
+
+// export all default c-types
+pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaAnyUserData)>> {
+    Ok(vec![c_int::get_export(lua)?])
+}
diff --git a/crates/lune-std-ffi/src/ffi/ffi_helper.rs b/crates/lune-std-ffi/src/ffi/ffi_helper.rs
index 87a905e..3e9f48a 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_helper.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_helper.rs
@@ -22,7 +22,7 @@ pub unsafe fn get_ptr_from_userdata(
     } else if userdata.is::<FfiRef>() {
         userdata.borrow::<FfiRef>()?.get_ptr()
     } else {
-        return Err(LuaError::external("asdf"));
+        return Err(LuaError::external("Unexpected userdata"));
     };
 
     let ptr = if let Some(t) = offset {
diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref.rs b/crates/lune-std-ffi/src/ffi/ffi_ref.rs
index 1150eb1..5e9a9f8 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_ref.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_ref.rs
@@ -12,6 +12,8 @@ use super::ffi_bounds::FfiRefBounds;
 // If it references an area managed by Lua,
 // the box will remain as long as this reference is alive.
 
+// Todo : how to impl ref == nullptr
+
 pub struct FfiRef {
     ptr: *mut (),
     range: Option<FfiRefBounds>,