ffi
The C ABI boundary. This is the only place in the standard library where raw platform types are allowed — every other module works in length-typed Str and String.
public module ffi::libc;
public module ffi::openssl;
| File | What it holds | Import |
|---|---|---|
libc | POSIX / glibc bindings, typed enums for closed-set arguments, the flag and errno constants | import std::ffi::libc; |
openssl | The OpenSSL 3.x subset net::tls drives | import std::ffi::openssl; |
Strings across the boundary
Cryo strings carry a length and no terminator; C strings carry a terminator and no length. Translation happens at the boundary, and there is no CString wrapper type — String does the job.
Going out, build a NUL-terminated copy and pass its pointer:
import std::ffi::libc;
import std::collections::string;
mut path: String = String::with_null(name);
const fd: i32 = libc::open(path.as_ptr() as string, libc::O_RDONLY, 0);
String::with_null(source) produces an owned copy that keeps a trailing NUL. as_cstr(mut &this) does the same in place on a string you already have: it writes the terminator at index length, reserving a spare byte if needed, without changing the logical length. It is idempotent and allocation-free once that byte exists.
Coming back, restore the length:
const view: Str = Str::from(c_string); // length via strlen; the NUL is excluded
Check pointers for null before you do it.
public function name() { ... }
public:Visible to any module that imports this one - the default for top-level items and for struct fields, so it is mostly written to be explicit. public: opens a visibility block that groups the members after it.
public module collections::array;In a directory's _module.cryo aggregator, lists a submodule and whether it is public. Importing the directory's namespace sees only the modules declared public here.
namespace std::ffi::openssl;ffi::openssl: bindings to the system OpenSSL (libssl + libcrypto).
import a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
mut name: Type = value;A mutable binding, reassignable after declaration - mutability is opt-in, const is the default. At module level it declares mutable global state. Before & it marks an exclusive reference or receiver instead.
mut path: Stringtype struct String<A = GlobalAlloc> {
buffer: RawBuffer<u8, A>;
length: u64;
}String: owned, heap-allocated, length-typed UTF-8.
std::collections::stringstatic String::with_null(source: Str) -> StringConstruct a NUL-terminated copy of source for handing to C. The terminating NUL lives in the buffer (so as_ptr() yields a valid const char*) but is excluded from the logical length, so as_str() / length() see only source. Matches C semantics on an interior NUL: the C side stops reading at the first one.
const NAME: Type = value;At module level, a true compile-time constant, conventionally SCREAMING_SNAKE_CASE. (mut at module level declares mutable global state instead.)
const fd: i32extern "C" function open(path: u8*, flags: i32, mode: u32) -> i32mut path: StringString::as_ptr(&this) -> u8*value as TypeExplicit conversion: between numeric types, or reinterpreting one pointer type as another. Cryo never converts implicitly, and as inserts no range checks - a narrowing cast is the programmer's responsibility.
primitive stringNUL-terminated raw string (u8*), matching the C ABI. Length-typed text is Str / String.
const O_RDONLY: i32 = 0;const view: Strtype struct Str {
bytes: u8*;
length: u64;
}Str: a borrowed UTF-8 string slice.
std::collections::strstatic Str::from<T>(src: T) -> StrConstruct a Str view from a text source, dispatched on T at compile time: - string - a NUL-terminated C-string (literal or an i8*/u8* cast to string); length via strlen, the NUL is not part of the view. - Slice<u8> - an existing byte slice, borrowed verbatim.
mut &this
mut &TypeAn exclusive, mutating borrow. As a receiver, mut &this lets the method modify the fields - and callers can see that from the signature alone. As a parameter type, mut &T lets the callee mutate through the reference while the caller keeps ownership.
&this
mut &this
thisThe receiver of a method. Its first parameter declares how the method takes it: &this borrows read-only, mut &this borrows exclusively, and this / mut this move the value in and consume it. In the body, this.field reaches the members.