Avatar ·

Как сгенерировать случайное число в заданном диапазоне в Rust?

Всем привет! Я только начал изучать Rust и столкнулся с проблемой генерации случайных чисел. В других языках (например, Python или JavaScript) это делается одной функцией, а в Rust я запутался. Мне нужно сгенерировать случайное целое число от 1 до 100 (включительно) для простой игры-угадайки. Я слышал, что нужно использовать крейт `rand`, но я не понимаю, как его правильно подключить и использовать. Вот что я пробовал: ```rust // Пытаюсь использовать rand, но не знаю, как импортировать fn main() { let secret_number = rand::thread_rng().gen_range(1, 101); println!("Секретное число: {}", secret_number); } ``` Компилятор выдаёт ошибку: `error[E0433]: failed to resolve: use of undeclared crate or module 'rand'`. Я добавил `rand = "0.8"` в `Cargo.toml`, но всё равно не работает. Может быть, я неправильно подключаю? Или синтаксис изменился? Также я видел, что в старых версиях Rust использовали `rand::Rng`, а в новых — что-то другое. Подскажите, пожалуйста, как правильно генерировать случайные числа в современном Rust (2024 год)? Нужен полный, рабочий пример с подключением крейта. Спасибо! <<>>

Полный ответ: генерация случайных чисел в Rust с крейтом `rand`

Вы на правильном пути! Для генерации случайных чисел в Rust действительно используется крейт rand. Однако с момента вашего вопроса синтаксис немного изменился (особенно в версии 0.8+). Давайте разберём всё по шагам.

Шаг 1: Добавление зависимости в Cargo.toml

Откройте файл Cargo.toml вашего проекта и добавьте под секцией [dependencies] строку:

[dependencies]
rand = "0.8"

После этого выполните cargo build — Cargo автоматически скачает крейт.

Шаг 2: Импорт и использование в коде

В современном Rust (версия 1.50+) метод gen_range принимает диапазон в виде start..=end (включительно) или start..end (исключительно). Вот рабочий пример:

use rand::Rng;

fn main() {
    // Создаём генератор случайных чисел (thread-local)
    let mut rng = rand::thread_rng();
    
    // Генерация числа от 1 до 100 включительно
    let secret_number: u32 = rng.gen_range(1..=100);
    
    println!("Секретное число: {}", secret_number);
    
    // Другие примеры:
    let float_num: f64 = rng.gen_range(0.0..1.0); // вещественное от 0 до 1 (не включая 1)
    let dice_roll: u8 = rng.gen_range(1..=6);    // бросок кубика
}

Почему ваш код не работал?

  • Не хватало импорта: нужно добавить use rand::Rng; — это трейт, который предоставляет методы gen_range, gen и другие.
  • Устаревший синтаксис: в версии rand 0.8 метод gen_range принимает один аргумент-диапазон (1..=100), а не два числа (1, 101). Двухаргументный синтаксис был в rand 0.7 и удалён.
  • Не использовался mut: thread_rng() возвращает изменяемый генератор, поэтому переменная должна быть let mut rng.

Дополнительные возможности

Крейт rand умеет генерировать разные типы:

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    
    // Случайное число из заданного набора (выбор элемента)
    let choices = ['A', 'B', 'C', 'D'];
    let random_choice = choices[rng.gen_range(0..choices.len())];
    
    // Случайный bool
    let coin_flip: bool = rng.gen();
    
    // Случайное число с нормальным распределением (требуется крейт rand_distr)
    // use rand_distr::{Normal, Distribution};
    // let normal = Normal::new(0.0, 1.0).unwrap();
    // let sample = normal.sample(&mut rng);
}

Полный рабочий пример для игры-угадайки

use rand::Rng;
use std::io;

fn main() {
    println!("Угадайте число от 1 до 100!");
    
    let secret_number = rand::thread_rng().gen_range(1..=100);
    
    loop {
        println!("Введите ваше предположение:");
        
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Ошибка чтения строки");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
        
        match guess.cmp(&secret_number) {
            std::cmp::Ordering::Less => println!("Слишком мало!"),
            std::cmp::Ordering::Greater => println!("Слишком много!"),
            std::cmp::Ordering::Equal => {
                println!("Вы угадали!");
                break;
            }
        }
    }
}

Важно: Если вы используете rand версии 0.7 или ниже, синтаксис будет другим. Всегда проверяйте документацию по крейту на docs.rs.

<<>>

Альтернативный подход: генерация без крейта rand (для простых случаев)

Если вам нужно только одно случайное число и вы не хотите подключать внешний крейт, можно использовать встроенный std::time::SystemTime для генерации псевдослучайного числа на основе времени. Однако это не рекомендуется для криптографии или игр, где нужна настоящая случайность, но для обучения подойдёт.

use std::time::{SystemTime, UNIX_EPOCH};

fn simple_random(min: u32, max: u32) -> u32 {
    let duration = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards");
    
    // Берём наносекунды и используем как seed
    let nanos = duration.subsec_nanos() as u64;
    let range = max - min + 1;
    
    // Простой линейный конгруэнтный генератор (для демонстрации)
    let result = (nanos * 6364136223846793005 + 1442695040888963407) % (range as u64);
    
    min + (result as u32)
}

fn main() {
    let secret = simple_random(1, 100);
    println!("Случайное число (без крейта): {}", secret);
}

Минусы этого подхода:

  • Числа будут повторяться, если вызывать функцию быстро (в пределах одной наносекунды).
  • Распределение неравномерное — некоторые числа будут выпадать чаще.
  • Не подходит для серьёзных проектов.

Поэтому для реальных задач всегда используйте крейт rand — он оптимизирован, безопасен и даёт качественные случайные числа.