De Ruby à Rust
Philosophies fondamentales
Deux objectifs distincts

Points communs et concepts partagés
Malgré leurs philosophies différentes, Ruby et Rust partagent des concepts modernes qui facilitent la transition de l'un à l'autre.

Les grandes différences structurelles
Les différences fondamentales expliquent leurs cas d'usage respectifs.

La cohérence d’apprentissage
Du prototypage à la production

Similitudes du code
Similitudes dans le langage et la syntaxe

Traits (Rust) vs Modules/Mixins (Ruby)

Focus : Syntaxe des blocs et closures
Blocs avec paramètres
ruby
# Ruby
numbers.map { |x| x * 2 }
numbers.select { |x| x > 5 }rust
// Rust
numbers.iter().map(|x| x * 2)
numbers.iter().filter(|&x| x > 5)Blocs multi-lignes
ruby
# Ruby
items.each do |item|
puts item.name
item.process
endrust
// Rust
items.iter().for_each(|item| {
println!("{}", item.name);
item.process();
});Pattern matching

Voir vidéo et Post

Match sur valeurs
ruby
# Ruby (depuis 2.7)
case value
in 0
"zero"
in 1..10
"small"
in x if x > 100
"big"
endrust
// Rust
match value {
0 => "zero",
1..=10 => "small",
x if x > 100 => "big",
_ => "other"
}Destructuration
ruby
# Ruby
case point
in {x: 0, y: 0}
"origin"
in {x: x, y: 0}
"on x-axis at #{x}"
endrust
// Rust
match point {
Point { x: 0, y: 0 } => "origin",
Point { x, y: 0 } => format!("on x-axis at {}", x),
_ => "somewhere else"
}Méthodes chaînées et programmation fonctionnelle
Transformations de collections

ruby
# Ruby
numbers
.select { |x| x.even? }
.map { |x| x * 2 }
.sumrust
// Rust
numbers
.iter()
.filter(|&x| x % 2 == 0)
.map(|x| x * 2)
.sum()Opérations complexes
ruby
# Ruby
users
.group_by(&:department)
.transform_values { |users| users.map(&:salary).sum }rust
// Rust
users
.iter()
.into_group_map_by(|user| &user.department)
.into_iter()
.map(|(dept, users)| (dept, users.iter().map(|u| u.salary).sum()))
.collect()Itérateurs et énumération

Création et utilisation d'itérateurs
ruby
# Ruby
(1..10)
.lazy
.map { |x| x * x }
.select { |x| x > 20 }
.first(3)rust
// Rust
(1..=10)
.map(|x| x * x)
.filter(|&x| x > 20)
.take(3)
.collect::<Vec<_>>()Itération avec index
ruby
# Ruby
items.each_with_index do |item, index|
puts "#{index}: #{item}"
endrust
// Rust
items.iter().enumerate().for_each(|(index, item)| {
println!("{}: {}", index, item);
});Conventions de nommage
Snake case

ruby
# Ruby
def calculate_total_price(items)
base_price = items.sum(&:price)
tax_amount = base_price * TAX_RATE
base_price + tax_amount
endrust
// Rust
fn calculate_total_price(items: &[Item]) -> f64 {
let base_price: f64 = items.iter().map(|item| item.price).sum();
let tax_amount = base_price * TAX_RATE;
base_price + tax_amount
}Gestion des collections
Création et manipulation

ruby
# Ruby
hash = { name: "John", age: 30 }
hash[:email] = "john@example.com"
hash.each { |k, v| puts "#{k}: #{v}" }rust
// Rust
use std::collections::HashMap;
let mut hash = HashMap::new();
hash.insert("name", "John");
hash.insert("age", "30");
hash.insert("email", "john@example.com");
hash.iter().for_each(|(k, v)| println!("{}: {}", k, v));Gestion des modules et namespaces
Organisation du code

ruby
# Ruby
module Authentication
class User
def authenticate(password)
# ...
end
end
endrust
// Rust
mod authentication {
pub struct User {
// fields
}
impl User {
pub fn authenticate(&self, password: &str) -> bool {
// ...
}
}
}Tests intégrés
Tests unitaires
ruby
# Ruby
require 'minitest/autorun'
class TestCalculator < Minitest::Test
def test_addition
assert_equal 4, Calculator.add(2, 2)
end
endrust
// Rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_addition() {
assert_eq!(Calculator::add(2, 2), 4);
}
}Ces similitudes syntaxiques facilitent grandement la transition entre les deux langages, même si les concepts sous-jacents (ownership, borrowing en Rust vs garbage collection en Ruby) sont très différents.
Différences du code
Gestion des erreurs et Options

Valeurs optionnelles
ruby
# Ruby
def find_user(id)
users.find { |u| u.id == id }
end
user = find_user(123)
puts user&.name || "Unknown"rust
// Rust
fn find_user(id: u32) -> Option<&User> {
users.iter().find(|u| u.id == id)
}
let user = find_user(123);
println!("{}", user.map_or("Unknown".to_string(), |u| u.name.clone()));Chaînage avec gestion d'erreur
ruby
# Ruby
result = fetch_data(id)
&.then { |data| process_data(data) }
&.then { |processed| save_data(processed) }rust
// Rust
let result = fetch_data(id)
.and_then(|data| process_data(data))
.and_then(|processed| save_data(processed));Métaprogrammation et macros
Génération de code
ruby
# Ruby
[:name, :email, :phone].each do |attr|
define_method("#{attr}_present?") do
!send(attr).nil? && !send(attr).empty?
end
endrust
// Rust
macro_rules! generate_presence_check {
($($field:ident),*) => {
$(
fn paste::paste! { [<$field _present>] }(&self) -> bool {
!self.$field.is_empty()
}
)*
};
}Définition et structures de classes

ruby
# Ruby
class Point
attr_accessor :x, :y
def initialize(x, y)
@x, @y = x, y
end
def distance_from_origin
Math.sqrt(x**2 + y**2)
end
endrust
// Rust
struct Point {
x: f64,
y: f64,
}
impl Point {
fn new(x: f64, y: f64) -> Self {
Point { x, y }
}
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}Similitude des outils
Similitudes dans les outils de développement

Détails des outils de développement

Comparaison complète : Ruby vs Rust pour le développement de bibliothèques
Structure des projets

Ruby (Gem)
simple_calculator/
├── lib/
│ ├── simple_calculator.rb
│ ├── simple_calculator/
│ │ ├── calculator.rb
│ │ └── converter.rb
├── spec/
│ ├── spec_helper.rb
│ ├── simple_calculator_spec.rb
│ ├── simple_calculator/
│ │ ├── calculator_spec.rb
│ │ └── converter_spec.rb
├── .yardopts
├── Gemfile
├── Rakefile
├── simple_calculator.gemspec
└── README.mdRust (Crate)
simple_calculator/
├── src/
│ ├── lib.rs
│ ├── calculator.rs
│ └── converter.rs
├── tests/
│ └── integration_tests.rs
├── Cargo.toml
├── Cargo.lock
└── README.mdCode source
Ruby (Gem)
lib/simple_calculator.rb
require "simple_calculator/calculator"
require "simple_calculator/converter"
# A simple calculator library for basic operations and conversions
#
# @example Basic usage
# SimpleCalculator.add(2, 3) # => 5
# SimpleCalculator.subtract(5, 3) # => 2
#
# @example Temperature conversion
# SimpleCalculator.celsius_to_fahrenheit(0) # => 32
module SimpleCalculator
VERSION = "0.1.0"
# Adds two numbers
#
# @param a [Numeric] First number
# @param b [Numeric] Second number
# @return [Numeric] Sum of a and b
# @raise [TypeError] If arguments are not numeric
def self.add(a, b)
Calculator.new.add(a, b)
end
# Subtracts second number from first
#
# @param a [Numeric] First number
# @param b [Numeric] Second number
# @return [Numeric] Result of a - b
# @raise [TypeError] If arguments are not numeric
def self.subtract(a, b)
Calculator.new.subtract(a, b)
end
# Converts Celsius to Fahrenheit
#
# @param celsius [Numeric] Temperature in Celsius
# @return [Numeric] Temperature in Fahrenheit
# @raise [TypeError] If argument is not numeric
def self.celsius_to_fahrenheit(celsius)
Converter.new.celsius_to_fahrenheit(celsius)
end
endlib/simple_calculator/calculator.rb
module SimpleCalculator
# Performs basic arithmetic operations
class Calculator
# Adds two numbers
#
# @param a [Numeric] First number
# @param b [Numeric] Second number
# @return [Numeric] Sum of a and b
# @raise [TypeError] If arguments are not numeric
def add(a, b)
validate_numbers(a, b)
a + b
end
# Subtracts second number from first
#
# @param a [Numeric] First number
# @param b [Numeric] Second number
# @return [Numeric] Result of a - b
# @raise [TypeError] If arguments are not numeric
def subtract(a, b)
validate_numbers(a, b)
a - b
end
private
# Validates that arguments are numeric
#
# @param a [Object] First object to check
# @param b [Object] Second object to check
# @raise [TypeError] If arguments are not numeric
def validate_numbers(a, b)
raise TypeError, "Arguments must be numbers"
unless a.is_a?(Numeric) && b.is_a?(Numeric)
end
end
endlib/simple_calculator/converter.rb
module SimpleCalculator
# Performs temperature conversions
class Converter
# Converts Celsius to Fahrenheit
#
# @param celsius [Numeric] Temperature in Celsius
# @return [Numeric] Temperature in Fahrenheit
# @raise [TypeError] If argument is not numeric
def celsius_to_fahrenheit(celsius)
raise TypeError, "Argument must be a number"
unless celsius.is_a?(Numeric)
(celsius * 9.0/5.0) + 32
end
end
endRust (Crate)
src/lib.rs
//! A simple calculator library for basic operations and conversions
//!
//! # Examples
//!
//! ```
//! // Basic usage
//! use simple_calculator::{add, subtract};
//! assert_eq!(add(2.0, 3.0), 5.0);
//! assert_eq!(subtract(5.0, 3.0), 2.0);
//!
//! // Temperature conversion
//! use simple_calculator::celsius_to_fahrenheit;
//! assert_eq!(celsius_to_fahrenheit(0.0), 32.0);
//! ```
pub mod calculator;
pub mod converter;
// Re-export main functionality
pub use calculator::Calculator;
pub use converter::Converter;
// Define custom error type
use thiserror::Error;
/// Errors that can occur in calculator operations
#[derive(Error, Debug)]
pub enum CalculatorError {
/// Indicates an invalid input was provided
#[error("Invalid input: {0}")]
InvalidInput(String),
}
/// Adds two numbers
///
/// # Examples
///
/// ```
/// use simple_calculator::add;
/// assert_eq!(add(2.0, 3.0), 5.0);
/// ```
pub fn add(a: f64, b: f64) -> f64 {
Calculator::new().add(a, b)
}
/// Subtracts the second number from the first
///
/// # Examples
///
/// ```
/// use simple_calculator::subtract;
/// assert_eq!(subtract(5.0, 3.0), 2.0);
/// ```
pub fn subtract(a: f64, b: f64) -> f64 {
Calculator::new().subtract(a, b)
}
/// Converts a temperature from Celsius to Fahrenheit
///
/// # Examples
///
/// ```
/// use simple_calculator::celsius_to_fahrenheit;
/// assert_eq!(celsius_to_fahrenheit(0.0), 32.0);
/// assert_eq!(celsius_to_fahrenheit(100.0), 212.0);
/// ```
pub fn celsius_to_fahrenheit(celsius: f64) -> f64 {
Converter::new().celsius_to_fahrenheit(celsius)
}src/calculator.rs
use crate::CalculatorError;
/// A calculator for basic arithmetic operations
pub struct Calculator;
impl Calculator {
/// Creates a new calculator
pub fn new() -> Self {
Calculator
}
/// Adds two numbers
///
/// # Examples
///
/// ```
/// use simple_calculator::Calculator;
/// let calc = Calculator::new();
/// assert_eq!(calc.add(2.0, 3.0), 5.0);
/// ```
pub fn add(&self, a: f64, b: f64) -> f64 {
a + b
}
/// Subtracts the second number from the first
///
/// # Examples
///
/// ```
/// use simple_calculator::Calculator;
/// let calc = Calculator::new();
/// assert_eq!(calc.subtract(5.0, 3.0), 2.0);
/// ```
pub fn subtract(&self, a: f64, b: f64) -> f64 {
a - b
}
/// Divides the first number by the second, returning an error if
/// the divisor is zero
///
/// # Examples
///
/// ```
/// use simple_calculator::Calculator;
/// let calc = Calculator::new();
/// assert_eq!(calc.safe_divide(6.0, 2.0).unwrap(), 3.0);
/// assert!(calc.safe_divide(6.0, 0.0).is_err());
/// ```
pub fn safe_divide(&self, a: f64, b: f64) -> Result<f64, CalculatorError> {
if b == 0.0 {
return Err(CalculatorError::InvalidInput("Division by zero"
.to_string()));
}
Ok(a / b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
let calc = Calculator::new();
assert_eq!(calc.add(2.0, 3.0), 5.0);
}
#[test]
fn test_subtract() {
let calc = Calculator::new();
assert_eq!(calc.subtract(5.0, 3.0), 2.0);
}
#[test]
fn test_safe_divide() {
let calc = Calculator::new();
assert_eq!(calc.safe_divide(6.0, 2.0).unwrap(), 3.0);
assert!(calc.safe_divide(6.0, 0.0).is_err());
}
}Tests
Ruby (RSpec)
spec/spec_helper.rb
require "simple_calculator"
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations
.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = "spec/examples.txt"
config.disable_monkey_patching!
config.warnings = true
if config.files_to_run.one?
config.default_formatter = "doc"
end
config.order = :random
Kernel.srand config.seed
endspec/simple_calculator_spec.rb
require 'spec_helper'
RSpec.describe SimpleCalculator do
it "has a version number" do
expect(SimpleCalculator::VERSION).not_to be nil
end
describe ".add" do
it "adds two numbers correctly" do
expect(SimpleCalculator.add(2, 3)).to eq(5)
end
it "raises TypeError for non-numeric arguments" do
expect { SimpleCalculator.add("2", 3) }
.to raise_error(TypeError)
end
end
describe ".subtract" do
it "subtracts two numbers correctly" do
expect(SimpleCalculator.subtract(5, 3)).to eq(2)
end
it "raises TypeError for non-numeric arguments" do
expect { SimpleCalculator.subtract(5, "3") }
.to raise_error(TypeError)
end
end
describe ".celsius_to_fahrenheit" do
it "converts 0°C to 32°F" do
expect(SimpleCalculator.celsius_to_fahrenheit(0)).to eq(32)
end
it "converts 100°C to 212°F" do
expect(SimpleCalculator.celsius_to_fahrenheit(100)).to eq(212)
end
it "raises TypeError for non-numeric arguments" do
expect { SimpleCalculator.celsius_to_fahrenheit("zero") }
.to raise_error(TypeError)
end
end
endRust (Tests)
tests/integration_tests.rs
use simple_calculator::{add, subtract, celsius_to_fahrenheit, Calculator};
#[test]
fn test_crate_level_functions() {
assert_eq!(add(2.0, 3.0), 5.0);
assert_eq!(subtract(5.0, 3.0), 2.0);
assert_eq!(celsius_to_fahrenheit(0.0), 32.0);
}
#[test]
fn test_calculator_safe_divide() {
let calc = Calculator::new();
let result = calc.safe_divide(10.0, 2.0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 5.0);
let error_result = calc.safe_divide(10.0, 0.0);
assert!(error_result.is_err());
}Configuration des projets
Ruby (Gem)
Gemfile
source "https://rubygems.org"
gemspec
group :development, :test do
gem "rspec", "~> 3.10"
gem "yard", "~> 0.9.27"
gem "redcarpet", "~> 3.5"
# Pour le rendu Markdown dans YARD
gem "rake", "~> 13.0"
endRakefile
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "yard"
RSpec::Core::RakeTask.new(:spec)
YARD::Rake::YardocTask.new do |t|
t.files = ["lib/**/*.rb"]
t.options = ["--markup", "markdown"]
t.stats_options = ["--list-undoc"]
end
task :default => [:spec, :yard]simple_calculator.gemspec
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "simple_calculator"
Gem::Specification.new do |spec|
spec.name = "simple_calculator"
spec.version = SimpleCalculator::VERSION
spec.authors = ["Example Author"]
spec.email = ["author@example.com"]
spec.summary = "A simple calculator gem"
spec.description = "Performs basic calculations and conversions"
spec.homepage = "https://github.com/example/simple_calculator"
spec.license = "MIT"
spec.files = Dir["lib/**/*", "README.md"]
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.10"
spec.add_development_dependency "yard", "~> 0.9.27"
end.yardopts
--markup markdown
--markup-provider redcarpet
--readme README.md
--title "SimpleCalculator Documentation"
--protected
--private
--embed-mixins
--output-dir doc/yardRust (Crate)
Cargo.toml
[package]
name = "simple_calculator"
version = "0.1.0"
edition = "2021"
authors = ["Example Author <author@example.com>"]
description = "A simple calculator library"
license = "MIT"
repository = "https://github.com/example/simple_calculator"
documentation = "https://docs.rs/simple_calculator"
readme = "README.md"
[dependencies]
thiserror = "1.0"
[dev-dependencies]
criterion = "0.3"
[lib]
name = "simple_calculator"
path = "src/lib.rs"Comparaison des outils de documentation
Ruby (YARD)

YARD est un outil de documentation pour Ruby qui génère une documentation HTML à partir des commentaires dans le code.
Installation et configuration
# Installation
gem install yard
# Génération de la documentation
yard doc
# ou
rake yardExemple de commentaire YARD
# Converts Celsius to Fahrenheit
#
# @param celsius [Numeric] Temperature in Celsius
# @return [Numeric] Temperature in Fahrenheit
# @example
# converter = Converter.new
# converter.celsius_to_fahrenheit(0) # => 32
# @raise [TypeError] If argument is not numeric
def celsius_to_fahrenheit(celsius)
# implementation
endRust (Rustdoc)
Rustdoc est intégré à Rust et génère automatiquement la documentation à partir des commentaires de documentation (doc comments).
Installation et configuration
# Aucune installation nécessaire, rustdoc est inclus avec Rust
# Génération de la documentation
cargo doc --no-deps --open
# Options courantes
cargo doc --document-private-items # Inclure les éléments privés
cargo doc --no-deps # Ne pas inclure les dépendancesExemple de commentaire Rustdoc
/// Converts a temperature from Celsius to Fahrenheit
///
/// # Examples
///
/// ```
/// use simple_calculator::Converter;
/// let converter = Converter::new();
/// assert_eq!(converter.celsius_to_fahrenheit(0.0), 32.0);
/// ```
pub fn celsius_to_fahrenheit(&self, celsius: f64) -> f64 {
// implementation
}Comparaison des systèmes de build et de tâches
Ruby (Rake)

Rake est un outil de build pour Ruby, similaire à Make, qui permet de définir des tâches.
Installation et configuration
# Installation
gem install rake
# Création d'un Rakefile
touch RakefileExemple de Rakefile
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "yard"
# Définition des tâches RSpec
RSpec::Core::RakeTask.new(:spec)
# Définition des tâches YARD
YARD::Rake::YardocTask.new do |t|
t.files = ["lib/**/*.rb"]
t.options = ["--markup", "markdown"]
t.stats_options = ["--list-undoc"]
end
# Tâche personnalisée
desc "Exécute les tests et vérifie la couverture de documentation"
task :quality => [:spec, :yard]
# Tâche par défaut
task :default => :qualityExécution des tâches
# Exécuter la tâche par défaut
rake
# Exécuter une tâche spécifique
rake spec
rake yard
rake quality
# Lister toutes les tâches disponibles
rake -TRust (Cargo)

Cargo est le gestionnaire de paquets et système de build de Rust, intégré au langage.
Installation et configuration
# Aucune installation nécessaire,
# Cargo est inclus avec Rust
# Création d'un nouveau projet
cargo new simple_calculator --libExemple de commandes Cargo
# Compiler le projet
cargo build
# Exécuter les tests
cargo test
# Générer la documentation
cargo doc
# Vérifier le code sans compiler
cargo check
# Publier sur crates.io
cargo publishPersonnalisation avec Cargo Make
Pour des tâches plus complexes, on peut utiliser Cargo Make, un outil tiers :
# Installation
cargo install cargo-make
# Création d'un
# fichier Makefile.toml
touch Makefile.tomlExemple de Makefile.toml
[tasks.format]
command = "cargo"
args = ["fmt", "--all"]
[tasks.lint]
command = "cargo"
args = ["clippy", "--all-targets", "--all-features"]
[tasks.test]
command = "cargo"
args = ["test"]
[tasks.doc]
command = "cargo"
args = ["doc", "--no-deps", "--open"]
[tasks.ci]
dependencies = [
"format",
"lint",
"test",
"doc"
]
[tasks.default]
dependencies = ["ci"]Exécution des tâches
# Exécuter la tâche par défaut
cargo make
# Exécuter une tâche spécifique
cargo make format
cargo make test
cargo make ciTableau comparatif
| Fonctionnalité | Ruby | Rust |
|---|---|---|
| Système de build | Rake | Cargo |
| Documentation | YARD | Rustdoc |
| Tests | RSpec | Tests intégrés + Cargo test |
| Gestion des dépendances | Bundler | Cargo |
| Publication | RubyGems | crates.io |
| Formatage | Rubocop | rustfmt |
| Analyse statique | Rubocop | Clippy |
| Exécution des tâches | rake task | cargo command |
| Personnalisation | Rakefile | Cargo Make (tiers) |
Comparaison détaillée des systèmes de documentation
| Aspect | YARD (Ruby) | Rustdoc (Rust) |
|---|---|---|
| Intégration | Externe, via gem | Intégré au langage |
| Syntaxe | Tags (@param, @return, etc.) | Markdown avec sections (#Examples, etc.) |
| Tests dans la doc | Non exécutables | Exécutés comme tests (cargo test --doc) |
| Génération | yard doc ou rake yard | cargo doc |
| Hébergement | Rubydoc.info (manuel) | docs.rs (automatique) |
| Thème | Personnalisable | Standard, peu personnalisable |
| Recherche | Basique | Avancée, avec index |
| Liens vers le code source | Oui | Oui |
| Graphiques | Diagrammes de classes | Non |
| Statistiques | Couverture de documentation | Non |
Exemple d'utilisation

Ruby
Installation de la gem
gem install simple_calculatorUtilisation
require 'simple_calculator'
# Utilisation des fonctions de module
puts SimpleCalculator.add(2, 3) # => 5
puts SimpleCalculator.subtract(5, 3) # => 2
puts SimpleCalculator.celsius_to_fahrenheit(0) # => 32
# Utilisation directe des classes
calculator = SimpleCalculator::Calculator.new
puts calculator.add(2, 3) # => 5
converter = SimpleCalculator::Converter.new
puts converter.celsius_to_fahrenheit(100) # => 212Rust
Installation du crate
cargo add simple_calculatorUtilisation
use simple_calculator::{add, subtract, celsius_to_fahrenheit, Calculator, Converter};
fn main() {
// Utilisation des fonctions de module
println!("{}", add(2.0, 3.0)); // => 5
println!("{}", subtract(5.0, 3.0)); // => 2
println!("{}", celsius_to_fahrenheit(0.0)); // => 32
// Utilisation directe des structures
let calculator = Calculator::new();
println!("{}", calculator.add(2.0, 3.0)); // => 5
// Gestion d'erreurs avec Result
match calculator.safe_divide(10.0, 2.0) {
Ok(result) => println!("10 / 2 = {}", result),
Err(e) => println!("Erreur: {}", e),
}
let converter = Converter::new();
println!("{}", converter.celsius_to_fahrenheit(100.0)); // => 212
}Workflow de développement

Ruby
Publication
# Mettre à jour la version dans lib/simple_calculator/version.rb
rake releaseDocumentation
bundle exec yard
open doc/yard/index.htmlTests
bundle exec rspecDéveloppement
# Éditer les fichiers dans lib/
vim lib/simple_calculator.rbCréation du projet
bundle gem simple_calculatorRust
Publication
# Mettre à jour la version dans Cargo.toml
cargo publishDocumentation
cargo doc --openTests
cargo testDéveloppement
# Éditer les fichiers dans src/
vim src/lib.rsCréation du projet
cargo new simple_calculator --libEcosystèmes & communautés
Similitudes dans la communauté
& gouvernance

Similitudes dans les domaines d'application

Similitudes dans l'apprentissage et adoption

Similitudes dans l'écosystème

Conclusion

