Перехід на Ruby з C та C++

Складно скласти маркований список того, як ваш код відрізнятиметься в Ruby від C чи C++, адже різниця доволі велика. Одна з причин — у тому, що рантайм Ruby робить дуже багато за вас. Ruby — це майже повна протилежність принципу C «ніяких прихованих механізмів»; сенс Ruby — полегшити роботу людини за рахунок того, що рантайм бере на себе більше задач. Якщо ви не профілюєте код для оптимізації, вам не потрібно взагалі турбуватися про “задоволення компілятора” під час використання Ruby.

З іншого боку, можна очікувати, що Ruby-код виконуватиметься значно повільніше, ніж “еквівалентний” C або C++ код. Водночас ви будете здивовані, наскільки швидко можна запустити програму на Ruby і як мало рядків коду потрібно, щоб її написати. Ruby набагато простіший за C++ — він вас дуже розпестить.

Ruby має динамічну типізацію, а не статичну — рантайм робить якомога більше під час виконання. Наприклад, вам не потрібно заздалегідь знати, які модулі програма Ruby буде “лінкувати” (тобто завантажувати та використовувати) або які методи вона викликатиме.

На щастя, Ruby і C мають здорові симбіотичні відносини. Ruby підтримує так звані “extension modules” — модулі, які можна використовувати у Ruby- програмах (і які ззовні виглядають і поводяться як звичайні Ruby-модулі), але написані на C. Так ви можете винести критичні до продуктивності частини вашого застосунку на Ruby у чистий C.

І, звісно, сам Ruby написаний на C.

Подібності з C

Як і в C, у Ruby…

  • Ви можете програмувати процедурно, якщо хочете (але за лаштунками це все одно буде об’єктно-орієнтовано).
  • Більшість операторів однакові (включно зі складеними присвоєннями та побітовими операторами). Але в Ruby немає ++ чи --.
  • Є __FILE__ і __LINE__.
  • Також є константи, хоча спеціального ключового слова const немає. Сталість забезпечується конвенцією імен — імена з великої літери є константами.
  • Рядки беруться в подвійні лапки.
  • Рядки змінювані.
  • Як і man-сторінки, більшість документації можна читати у терміналі — через команду ri.
  • Є подібний налагоджувач командного рядка.

Подібності з C++

Як і в C++, у Ruby…

  • Є здебільшого ті самі оператори (навіть ::). << часто використовується для додавання елементів до списку. Є одна відмінність: у Ruby ви ніколи не використовуєте -> — завжди ..
  • public, private і protected виконують подібні ролі.
  • Синтаксис наслідування — лише один символ, але це <, а не :.
  • Ви можете складати код у “модулі”, подібно до того, як у C++ використовується namespace.
  • Винятки працюють подібно, хоча назви ключових слів змінилися, щоб “захистити невинних”.

Відмінності від C

На відміну від C, у Ruby…

  • Вам не потрібно компілювати код. Ви просто запускаєте його безпосередньо.
  • Об’єкти мають строгі типи (а самі імена змінних типу не мають).
  • Немає макросів або препроцесора. Немає кастів. Немає вказівників (ані арифметики з вказівниками). Немає typedef, sizeof і enum.
  • Немає заголовкових файлів. Ви просто визначаєте функції (зазвичай їх називають “методами”) і класи в основних файлах з вихідним кодом.
  • Немає #define. Натомість використовуйте константи.
  • Усі змінні живуть у heap. Також вам не потрібно звільняти пам’ять вручну — цим займається збирач сміття.
  • Аргументи методів (тобто функцій) передаються за значенням, де значення завжди є посиланнями на об’єкти.
  • Замість #include <foo> або #include "foo" використовується require 'foo'.
  • Ви не можете опускатися до асемблера.
  • Немає крапок з комою в кінці рядків.
  • Ви обходитесь без дужок у виразах умов if і while.
  • Дужки для виклику методів (тобто функцій) часто необов’язкові.
  • Зазвичай не використовують фігурні дужки — багаторядкові конструкції (як цикли while) завершуються ключовим словом end.
  • Ключове слово do використовується для так званих “блоків”. Немає “оператора do”, як у C.
  • Термін “блок” означає інше. Це блок коду, який ви прив’язуєте до виклику методу, щоб тіло методу могло викликати цей блок під час виконання.
  • Немає оголошень змінних. Ви просто присвоюєте значення новим іменам у момент потреби.
  • При перевірці на істинність лише false і nil дають хибне значення. Усе інше — істинне (включно з 0, 0.0 і "0").
  • Немає char — це просто рядки довжиною 1.
  • Рядки не закінчуються нульовим байтом.
  • Літерали масивів беруться в квадратні дужки, а не у фігурні.
  • Масиви автоматично збільшуються, коли ви додаєте в них елементи.
  • Якщо додати два масиви, ви отримаєте новий, більший масив (звісно, виділений у heap), а не арифметику вказівників.
  • Часто все є виразом (тобто навіть while повертає значення rvalue).

Відмінності від C++

На відміну від C++, у Ruby…

  • Немає явних посилань. У Ruby кожна змінна — це лише автоматично розіменований іменник для певного об’єкта.
  • Об’єкти мають строгі, але динамічні типи. Рантайм визначає під час виконання, чи працює виклик методу.
  • “Конструктор” називається initialize, а не як ім’я класу.
  • Усі методи завжди віртуальні.
  • Імена “класових” (статичних) змінних завжди починаються з @@ (наприклад, @@total_widgets).
  • Ви не звертаєтеся до змінних-членів напряму — доступ до публічних змінних-членів (у Ruby їх називають атрибутами) здійснюється через методи.
  • Це self замість this.
  • Деякі методи закінчуються на ? або !. Це справжня частина імені методу.
  • Немає множинного наслідування як такого. Проте Ruby має “міксини” (тобто ви можете “успадковувати” всі методи екземпляра модуля).
  • Є певні конвенції регістру (наприклад, назви класів починаються з великої літери, змінні — з малої).
  • Дужки для викликів методів зазвичай необов’язкові.
  • Ви можете повторно відкривати клас у будь-який момент і додавати нові методи.
  • Немає потреби у шаблонах C++ (оскільки будь-який об’єкт можна присвоїти змінній, а типи все одно визначаються під час виконання). Немає і кастів.
  • Ітерація відбувається трохи інакше. У Ruby ви не використовуєте окремий об’єкт-ітератор (як vector<T>::const_iterator iter). Натомість використовуєте метод-ітератор контейнера (наприклад, each), який приймає блок коду, якому передає послідовні елементи.
  • Є лише два типи контейнерів: Array і Hash.
  • Немає перетворень типів. У Ruby ви, ймовірно, побачите, що вони й не потрібні.
  • Багатопотоковість вбудована, але починаючи з Ruby 1.8 це “green threads” (реалізовані лише в інтерпретаторі), а не нативні потоки.
  • Бібліотека юніт-тестування входить до стандартної поставки Ruby.