Макросы #define и #ifdef в Rust

Можно ли переписать этот код C++ на Rust?

#define is_unionfind_imported
class UnionFind {
};

class Kruskal {
#ifndef is_unionfind_imported
    #error "UnionFind not imported"
#endif
    UnionFind u;
};

int main() {
}

Почему, во-первых?

Я часто занимаюсь соревновательным программированием на Rust. В соревновательном программировании пользователи обычно регистрируют исходные коды часто используемых структур данных в виде сниппетов.

Например, в моем случае этот код вставляется из ~/snippets/union_find.rs, когда я выполняю команду :I unionfind в Vim:

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
struct UnionFind {
    /* ... */
}
impl UnionFind {
    /* ... */
}

Кроме того, когда я запускаю :I kruskal в Vim, этот код вставляется из ~/snippets/kruskal_method.rs:

#[derive(Debug, Clone)]
struct Kruskal {
    u: UnionFind,
    /* ... */
}
impl Kruskal {
    fn new(/* ... */) -> Self {
        /* ... */
        Self {
            u: UnionFind::new(0),
        }
    }
}

Проблема в том, что Kruskal зависит от UnionFind. Если я вручную запускаю :I UnionFind и :I Kruskal, проблем нет, но я часто забываю сначала импортировать UnionFind. Затем компилятор говорит

error[E0412]: cannot find type `UnionFind` in this scope

но я хотел бы сделать это сообщение об ошибке более удобным для пользователя (например, UnionFind is not imported. Execute :I unionfind in Vim.).

И обратите внимание

  • Модульная система не может использоваться в конкурентной среде программирования. (Можно отправить только один исходный файл.)

  • Я не хочу копировать UnionFind код в Kruskal фрагменте для удобства сопровождения.

  • Внедрение системы управления зависимостями в моем средстве вставки сниппетов (то есть в бэкенде команды :I) — это крайняя мера.

В Rust есть условная компиляция, основанная на параметрах конфигурации, но, насколько мне известно, их нельзя установить из исходного кода, только как флаги для rustc или в Cargo.toml.

isaactfa 18.06.2023 13:35
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
1
98
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Но я хотел бы сделать это сообщение об ошибке более удобным для пользователя (например, UnionFind не импортируется. Выполните: I unionfind в Vim.).

Изменение сообщений об ошибках невозможно напрямую в Rust.

Учитывая, что вы работаете с сниппетами, пробовали ли вы добавить UnionFind в свой сниппет Kruskal?

Вы также можете использовать модули в исходном файле.

Например, вы можете заменить ~/snippets/kruskal_method.rs на это:

mod kruskal {
    #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
    struct UnionFind {
        /* ... */
    }
    impl UnionFind {
        /* ... */
    }

    #[derive(Debug, Clone)]
    pub struct Kruskal {
        u: UnionFind,
        /* ... */
    }
    impl Kruskal {
        pub fn new(/* ... */) -> Self {
            /* ... */
            Self {
                u: UnionFind::new(0),
            }
        }
    }
}

use kruskal::Kruskal;

Таким образом, вы всегда будете получать UnionFind со своим кодом, но больше ничего не изменится.

Извините, если мое намерение было двусмысленным, но ОП говорит, что я не хочу копировать код UnionFind во фрагменте Kruskal для удобства обслуживания. Я имел в виду, что не хочу встраивать UnionFind в Kruskal.

ynn 18.06.2023 14:46

Прошу прощения, я пропустил это в вашем посте. В таком случае, я не думаю, что вашу проблему можно решить, вам придется полагаться на сообщения об ошибках Rust. Возможно, вы все еще можете использовать блоки mod {} и операторы использования, но я не проверял, какое сообщение об ошибке будет возвращено.

Aaron Dewes 18.06.2023 19:37
Ответ принят как подходящий

Я придумал хакерское решение.

#![allow(unused)]

mod placeholders {
    pub struct UnionFind;
    pub const UNION_FIND_IMPORTED: bool = false;
}

use placeholders::*;

// Start snippet
struct Kruskal {
    u: UnionFind,
}
const _: () = if !UNION_FIND_IMPORTED {
    panic!("UnionFind is not imported. Execute :I unionfind in Vim.");
};
// End snippet

// Start snippet
// struct UnionFind;
// const UNION_FIND_IMPORTED: bool = true;
// End snippet

fn main() {
    println!("hello");
}

Это работает следующим образом: когда фрагмент UnionFind отсутствует, он извлекается из импорта glob. Это означает, что UnionFind присутствует всегда. Чтобы проверить, правильный ли он, константа UNION_FIND_IMPORTED присутствует рядом с обеими UnionFind структурами. Затем это время компиляции оценивается с помощью const _: (), что вызывает панику компиляции, когда константа импорта имеет значение false.

Плюсы:

  • Пользовательское сообщение об ошибке
  • Отдельный файл
  • Аргументы CLI не нужны

Минусы:

  • Требуется, чтобы у вас был модуль-заполнитель со всеми вашими типами и теми же реализованными методами и чертами, что и у ваших реальных типов, чтобы компилятор не останавливался перед оценкой константы.
  • Дополнительные константы

Спасибо. Я думаю, что это соответствует моей потребности, хотя афера требует, чтобы у вас был модуль-заполнитель со всеми вашими типами и теми же реализованными методами и чертами, что и ваши настоящие типы, ну... может быть, слишком тяжело использовать в реальности. Кстати, во втором фрагменте, может быть, pub лишнее?

ynn 19.06.2023 03:25

@ynn Да, паб был ошибкой копирования и вставки. Хотя особо ни на что не влияет.

drewtato 19.06.2023 04:53

Другие вопросы по теме