Open-Source Wikis

/

Ruby

/

How to contribute

/

Patterns and conventions

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 struct definitions in some places. Match nearby code.
  • Typedefs: rb_xxx_t for opaque types, RUBY_xxx for macros. Public types live in include/ruby/.
  • Static analysis: the build adds a long list of warning flags; PRs are expected to compile cleanly under -Wall -Wextra and the project's custom warnings.
  • Doxygen comments: header files use Doxygen @param/@return/@retval extensively. 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 the flags bits).
  • RTEST(v) — truthiness (anything that isn't Qfalse or Qnil).
  • 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:

  1. C implementation in the corresponding *.c (e.g., array.c).
  2. 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:

  1. Mark the reference during GC: register a rb_data_type_t with a dmark callback that calls rb_gc_mark(v) for each ref.
  2. Use write barriers on assignment: RB_OBJ_WRITE(parent, &parent->field, child) instead of plain parent->field = child. The barrier informs the generational GC.
  3. Pin if needed: rb_gc_register_mark_object(v) to keep v alive forever; rb_gc_register_address(&ptr) to hold a global root through ptr.

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 (from parse.y via Lrama)
  • prism/prism.c and prism/templates/output/* (from Prism templates)
  • vm_exec.c, vmtc.inc, opt_sc.inc, optunifs.inc (from insns.def via tool/ruby_vm/)
  • enc/trans/*.c (from transcoding tables in tool/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_* in array.c, str_* in string.c).
  • Public C functions: rb_<short>_<verb> (rb_ary_push, rb_str_cat).
  • Macros: UPPER_SNAKE_CASE in headers, often shadowed by lowercase inline functions in modern code.
  • Ruby-level constants: CamelCase. Class names mirror the file (Arrayarray.c).

Documenting the change

For a user-visible behaviour change:

  • Update NEWS.md under 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.md if extension authors will care.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Ruby wiki | Factory