Руководство разработчика Node.js о Rust

В нашей предыдущей статье мы обсуждали мощную систему типов Rust с точки зрения разработчика Node.js. Вы можете найти статью здесь:



Опираясь на нашу предыдущую статью, в которой была представлена ​​мощная система типов Rust, в этой статье мы углубимся в основные функции Rust: структуры, свойства, перечисления и коллекции, с примерами и сравнениями с Node.js.

Структуры

В Rust структуры — это настраиваемые типы данных, которые позволяют вам группировать связанные фрагменты данных. Они похожи на объекты JavaScript с точки зрения функциональности, но имеют другой синтаксис.

Пример структуры Rust:

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        email: String::from("user@example.com"),
        username: String::from("username123"),
        active: true,
        sign_in_count: 1,
    };
}

Пример объекта Node.js:

class User {
    constructor(email, username, active, signInCount) {
        this.email = email;
        this.username = username;
        this.active = active;
        this.signInCount = signInCount;
    }
}

const user1 = new User("user@example.com", "username123", true, 1);

В примере на Rust мы определяем структуру User с полями и их соответствующими типами. В основной функции мы создаем экземпляр структуры User, предоставляя значения для каждого поля. В примере Node.js мы определяем класс User с помощью конструктора и создаем экземпляр, используя ключевое слово new.

Черты

Трейты в Rust похожи на интерфейсы в других языках, что позволяет вам определять общее поведение для разных типов. Они полезны для реализации полиморфизма, и их можно сравнить с наследованием на основе прототипов в JavaScript.

Пример признака ржавчины:

trait Speak {
    fn speak(&self);
}

struct Human {
    name: String,
}

struct Dog {
    name: String,
}

impl Speak for Human {
    fn speak(&self) {
        println!("Hello, my name is {}.", self.name);
    }
}

impl Speak for Dog {
    fn speak(&self) {
        println!("Woof! My name is {}.", self.name);
    }
}

fn main() {
    let human = Human { name: String::from("Alice") };
    let dog = Dog { name: String::from("Buddy") };

    human.speak();
    dog.speak();
}

Пример интерфейса Node.js:

class Speakable {
    speak() {
        throw new Error("speak method not implemented");
    }
}

class Human extends Speakable {
    constructor(name) {
        super();
        this.name = name;
    }

    speak() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}

class Dog extends Speakable {
    constructor(name) {
        super();
        this.name = name;
    }

    speak() {
        console.log(`Woof! My name is ${this.name}.`);
    }
}

const human = new Human("Alice");
const dog = new Dog("Buddy");

human.speak();
dog.speak();

В примере Rust мы определяем трейт Speak с помощью одного метода speak. Затем мы определяем две структуры, Human и Dog, и реализуем трейт Speak для каждой из них. В примере Node.js мы достигаем аналогичного результата, используя наследование классов.

перечисления

Перечисления в Rust позволяют определить тип, который может представлять один из нескольких вариантов. Это полезно для создания структур данных, которые могут содержать различные типы данных. В JavaScript вы можете достичь аналогичной функциональности, используя помеченные объединения или размеченные объединения.

Пример перечисления в Rust:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn process_message(message: Message) {
    match message {
        Message::Quit => println!("Quit"),
        Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
        Message::Write(text) => println!("Write: {}", text),
        Message::ChangeColor(r, g, b) => println!("Change color to red: {}, green: {}, blue: {}", r, g, b),
    }
}

fn main() {
    let message = Message::Write(String::from("Hello, Rust!"));
    process_message(message);
}

Пример размеченного объединения Node.js:

class Message {
    constructor(type) {
        this.type = type;
    }
}

class Quit extends Message {
    constructor() {
        super("Quit");
    }
}

class Move extends Message {
    constructor(x, y) {
        super("Move");
        this.x = x;
        this.y = y;
    }
}

class Write extends Message {
    constructor(text) {
        super("Write");
        this.text = text;
    }
}

class ChangeColor extends Message {
    constructor(r, g, b) {
        super("ChangeColor");
        this.r = r;
        this.g = g;
        this.b = b;
    }
}

function processMessage(message) {
    switch (message.type) {
        case "Quit":
            console.log("Quit");
            break;
        case "Move":
            console.log(`Move to x: ${message.x}, y: ${message.y}`);
            break;
        case "Write":
            console.log(`Write: ${message.text}`);
            break;
        case "ChangeColor":
            console.log(`Change color to red: ${message.r}, green: ${message.g}, blue: ${message.b}`);
            break;
    }
}

const message = new Write("Hello, Node.js!");
processMessage(message);

В примере Rust мы определяем enum Message с четырьмя вариантами, каждый из которых представляет различный тип сообщения. Затем мы создаем функцию process_message, которая использует сопоставление с образцом для обработки каждого варианта. В примере Node.js мы используем базовый класс Message и создаем подклассы для каждого варианта, используя свойство type для функциональности размеченного объединения.

Коллекции

Rust предлагает несколько встроенных типов коллекций для хранения и организации данных, таких как Vec (растущий массив), HashMap (хранилище ключ-значение) и HashSet (набор уникальных значений). В Node.js вы можете использовать собственные структуры данных, такие как Array, Map и Set, для аналогичных целей.

Пример коллекций Rust:

use std::collections::{HashMap, HashSet};

fn main() {
    let mut vec = Vec::new();
    vec.push(1);
    vec.push(2);
    vec.push(3);

    let mut hashmap = HashMap::new();
    hashmap.insert(String::from("Blue"), 10);
    hashmap.insert(String::from("Yellow"), 20);

    let mut hashset = HashSet::new();
    hashset.insert(1);
    hashset.insert(2);
    hashset.insert(3);
}

Пример коллекций Node.js:

const array = [1, 2, 3];

const map = new Map();
map.set("Blue", 10);
map.set("Yellow", 20);

const set = new Set([1, 2, 3]);

В примере Rust мы импортируем и создаем Vec, HashMap и HashSet из стандартной библиотеки. После этого мы вставляем значения в каждую из этих коллекций. Между тем, пример Node.js демонстрирует, как добиться аналогичной функциональности, используя собственные структуры данных, такие как Array, Map и Set.

Заключение

На протяжении всей этой статьи мы исследовали некоторые важные функции Rust, включая структуры, трейты, перечисления и коллекции, проводя сравнения с концепциями Node.js. Нашей целью было дать вам более глубокое понимание внутренней работы Rust и того, как вы можете эффективно использовать его возможности. Как разработчик Node.js, изучение Rust может расширить ваш кругозор, улучшить навыки решения проблем, повысить производительность и позволить вам писать более безопасный и параллельный код. Удачного кодирования!