ruby/ruby
Patterns and conventions
CRuby is a long-lived C codebase with a distinct dialect of its own. This page captures the patterns you'll meet repeatedly.
C dialect
- Indentation: 4 spaces, no tabs in new code. Existing files vary — match the file you're editing.
- Brace style: K&R for functions, Allman for
structdefinitions in some places. Match nearby code. - Typedefs:
rb_xxx_tfor opaque types,RUBY_xxxfor macros. Public types live ininclude/ruby/. - Static analysis: the build adds a long list of warning flags; PRs are expected to compile cleanly under
-Wall -Wextraand the project's custom warnings. - Doxygen comments: header files use Doxygen
@param/@return/@retvalextensively. Match neighbours.
VALUE conventions
A VALUE (include/ruby/internal/value.h) is a pointer-sized tagged integer:
| Bit pattern | Meaning |
|---|---|
xxx...x1 |
A Fixnum integer (low bit set) |
0 |
Qfalse |
8 |
Qnil |
20 |
Qtrue |
0x20 |
Qundef (sentinel; not a real value) |
| Otherwise | Pointer to a heap object |
Helpers:
RB_FIXNUM_P(v),RB_NIL_P(v),RB_SPECIAL_CONST_P(v)— type tests.INT2FIX(n),LONG2FIX(n),FIX2INT(v),FIX2LONG(v)— Fixnum conversions.RB_TYPE_P(v, T_ARRAY)— heap object class check (uses theflagsbits).RTEST(v)— truthiness (anything that isn'tQfalseorQnil).StringValue(v),Check_Type(v, T_STRING)— coerce or assert.
Always go through these macros instead of poking the VALUE bits directly.
Adding a method to a core class
Most methods live in two places:
- C implementation in the corresponding
*.c(e.g.,array.c). - Class registration in the same file's
Init_*function.
Example skeleton (in array.c):
static VALUE
ary_my_new_method(VALUE self, VALUE arg)
{
Check_Type(arg, T_FIXNUM);
long n = FIX2LONG(arg);
/* ... */
return self;
}
void
Init_Array(void)
{
/* ... existing definitions ... */
rb_define_method(rb_cArray, "my_new_method", ary_my_new_method, 1);
}For methods with complex Ruby-side logic (e.g., needing each semantics), put the body in a sibling .rb file (array.rb, hash.rb, etc.). These are compiled into the binary at build time by tool/mk_builtin_loader.rb. Use Primitive.foo calls to descend into C primitives.
Method dispatch helpers
| C call | Ruby equivalent |
|---|---|
rb_funcall(recv, mid, n, args...) |
recv.send(mid, *args) |
rb_funcallv(recv, mid, n, argv) |
recv.send(mid, *argv) |
rb_yield(arg) |
yield arg from a C method |
rb_yield_values(n, ...) |
yield *values |
rb_block_call(recv, mid, ...) |
Call with a C block |
rb_obj_freeze(v) |
v.freeze |
mid is an ID — an interned symbol. Use rb_intern("foo") once and cache the result, or rb_intern_const("foo") for compile-time interning.
Error handling
Ruby uses longjmp for exceptions:
rb_raise(rb_eArgError, "expected at least %d, got %d", min, given);Use the predefined exception classes from error.c / include/ruby/error.h:
| Class | Variable | Use |
|---|---|---|
ArgumentError |
rb_eArgError |
bad argument count or value |
TypeError |
rb_eTypeError |
wrong type passed |
IndexError |
rb_eIndexError |
out-of-range access |
RangeError |
rb_eRangeError |
numerical out-of-range |
RuntimeError |
rb_eRuntimeError |
generic catch-all |
NotImplementedError |
rb_eNotImpError |
feature missing on platform |
Errno::* |
per-errno | OS errors (use rb_sys_fail) |
To make a region exception-safe (run cleanup even if Ruby raises):
rb_ensure(body_func, body_arg, ensure_func, ensure_arg);Or rb_protect to catch a Ruby exception in C. Both are in eval.c.
GC awareness
Every heap allocation that holds a reference to another VALUE must:
- Mark the reference during GC: register a
rb_data_type_twith admarkcallback that callsrb_gc_mark(v)for each ref. - Use write barriers on assignment:
RB_OBJ_WRITE(parent, &parent->field, child)instead of plainparent->field = child. The barrier informs the generational GC. - Pin if needed:
rb_gc_register_mark_object(v)to keepvalive forever;rb_gc_register_address(&ptr)to hold a global root throughptr.
TypedData is the modern way to wrap a C pointer in a Ruby object:
static const rb_data_type_t my_type = {
"MyThing",
{my_mark, my_free, my_memsize, my_compact},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};RUBY_TYPED_WB_PROTECTED advertises that the object never assigns child VALUEs without a barrier — required for shape-friendly objects.
Threads, GVL, and signals
The single most common source of bugs in C extensions: don't block while holding the GVL.
/* Wrong: blocks all Ruby threads */
ssize_t n = read(fd, buf, len);
/* Right: releases the GVL during the syscall */
ssize_t n = (ssize_t)rb_thread_call_without_gvl(
blocking_read, &args,
RUBY_UBF_IO, /* unblock function */
NULL);RUBY_UBF_IO and RUBY_UBF_PROCESS are predefined unblock functions that signal the thread to wake.
For pure-IO, prefer rb_io_wait(io, events, timeout) — it integrates with the Fiber Scheduler if one is installed.
Encoding-aware string operations
A Ruby String carries an Encoding:
VALUE str = rb_str_new_cstr("hello"); /* US-ASCII */
VALUE u = rb_utf8_str_new_cstr("こんにちは"); /* UTF-8 */For string operations that interpret characters (case folding, chars, length, regex matches), use the encoding-aware helpers in encoding.c (rb_enc_strlen, rb_enc_codelen, rb_enc_codepoint_len). Avoid RSTRING_LEN for character counting except in ASCII-only contexts.
Encoding::ASCII_8BIT (the "binary" encoding) signals "treat as bytes". It composes specially with all other encodings — see enc_compatible in encoding.c.
Source generation
Several files in this repo are generated. Don't edit:
parse.c(fromparse.yvia Lrama)prism/prism.candprism/templates/output/*(from Prism templates)vm_exec.c,vmtc.inc,opt_sc.inc,optunifs.inc(frominsns.defviatool/ruby_vm/)enc/trans/*.c(from transcoding tables intool/transcode-tblgen.rb)revision.h(from git)
If you need to change one of these, change the source and rerun make srcs.
Naming
- Internal C functions:
lower_snake_case. Many start with the file's prefix (ary_*inarray.c,str_*instring.c). - Public C functions:
rb_<short>_<verb>(rb_ary_push,rb_str_cat). - Macros:
UPPER_SNAKE_CASEin headers, often shadowed by lowercase inline functions in modern code. - Ruby-level constants:
CamelCase. Class names mirror the file (Array→array.c).
Documenting the change
For a user-visible behaviour change:
- Update
NEWS.mdunder the right section. - Add or update a spec in
spec/ruby/. - Add a focused regression test in
test/ruby/test_<area>.rb.
For a public C API change:
- Doxygen-comment the new function in its
include/ruby/header. - Mention it in
NEWS.mdif extension authors will care.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.