RAII and wrapping a C library

There is an example in the Julia doc of constructing, destructing a GSL permutation object using ccall:
https://docs.julialang.org/en/stable/manual/calling-c-and-fortran-code/#Some-Examples-of-C-Wrappers-1

In this example, (1) it is possible to create a Julia permutation object which is not valid in a GSL call and (2) if the Julia object is garbage collected, the underlying object managed by the C library will never be freed.

For my wrapper of the CALCEPH lib, I have come up with the following RAII design using inner constructor/finalizers.

type CalcephEphem
   data :: Ptr{Void}
   function CalcephEphem(files::Vector{<:AbstractString})
      ptr = ccall((:calceph_open_array, libcalceph), Ptr{Void}, (Int, Ptr{Ptr{UInt8}}), length(files), files)
      if (ptr == C_NULL)
         error("Unable to open ephemeris file(s)!")
      end
      obj = new(ptr)
      finalizer(obj,CalcephEphemDestructor) # register object destructor
      return obj
   end
end

# to be called by gc when cleaning up
# not in the exposed interface but can be called with finalize(e)
function CalcephEphemDestructor(e::CalcephEphem)
   if (e.data == C_NULL) 
      return
   end
   ccall((:calceph_close, libcalceph), Void, (Ptr{Void},), e.data) 
   e.data = C_NULL
   return  
end

CalcephEphem(file::AbstractString) = CalcephEphem([file])

function CalcephPrefetch(e::CalcephEphem) 
    if (e.data == C_NULL)
       error("Ephemeris object is not propely initialized.")
    end
    stat = ccall((:calceph_prefetch, libcalceph), Int, (Ptr{Void},), e.data)
    if (stat == 0)
       error("Unable to prefetch ephemeris!")
    end
    return
end

export CalcephEphem, CalcephPrefetch

Is this a proper way of doing this sort of thing in Julia or should I prefer the GSL example way in the docs?

Thank you.

I think both approaches are fine. Your approach is commonly used e.g. in HDF5.jl which wraps the hdf5 C library. If its just memory then the approach is the way to go. For other resources one might want to have more control over the destruction. In that case I usually implement an explicit destructor and document that it is mandatory calling it.

Do note that there is no guarantee about finalization order or when the finalizer will run like there is with RAII in C++. So it is discouraged to rely on those properties for program behavior (even if you can make something work the way you want with current behavior).