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

Есть ли способ распечатать полный список доступных членов типа или экземпляра в Rust?

Например:

  • В Python я могу использовать print(dir(object))
  • В C Clang имеет Python API, который может анализировать код C и анализировать его.

Будучи незнакомым с инструментами Rust, мне интересно знать, есть ли какой-нибудь способ сделать это, либо во время выполнения, либо во время компиляции, либо с помощью функций компилятора (например, макросов), либо с использованием внешних инструментов.

Этот вопрос намеренно широк, потому что точный метод не важен. На любом языке принято хотеть найти все переменные методы/функции. Не зная Руста хорошо, я не ограничиваю вопрос конкретными методами для обнаружения.

Причина, по которой я не определяю точный метод, заключается в том, что я предполагаю, что IDE понадобится эта информация, поэтому для ее поддержки (в конечном итоге) потребуется несколько видов самоанализа. Насколько я знаю, в Rust есть нечто подобное.

Я не думаю, что это дубликат полей Get типа struct в макросе, так как этот ответ может включать использование внешних инструментов (не обязательно макросов).

Ответ 1

  Есть ли способ распечатать полный список доступных членов типа или экземпляра в Rust?

В настоящее время нет такого встроенного API, чтобы вы могли получить поля во время выполнения. Однако вы можете получить поля двумя разными способами.

  1. Декларативные макросы
  2. Процедурные макросы

Решение с помощью декларативного макроса

macro_rules! generate_struct {
    ($name:ident {$($field_name:ident : $field_type:ty),+}) => {
        struct $name { $($field_name: $field_type),+ }
        impl $name {
            fn introspect() {
            $(
            let field_name = stringify!($field_name);
            let field_type = stringify!($field_type);
               println!("Field Name: {:?} , Field Type: {:?}",field_name,field_type);
            )*
            }
        }
    };
}

generate_struct! { MyStruct { num: i32, s: String } }

fn main() {
    MyStruct::introspect();
}

Это даст вам вывод:

Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"

площадка


Решение с использованием процедурного макроса

Поскольку процедурные макросы более сложны, чем декларативные макросы, перед началом работы лучше прочитать некоторые ссылки (ref1, ref2, ref3).

Мы напишем custom derive с именем "Instrospect". Для создания этого пользовательского производного нам нужно проанализировать нашу структуру как TokenStream с помощью syn crate.

#[proc_macro_derive(Introspect)]
pub fn derive_introspect(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemStruct);

    // ...
}

Поскольку наш ввод может быть проанализирован как ItemStruct, а в ItemStruct есть метод fields(), мы можем использовать его для получения полей нашей структуры.

После того как мы получим эти поля, мы можем проанализировать их как именованные и напечатать их field name и field type соответственно.

input
    .fields
    .iter()
    .for_each(|field| match field.parse_named() {
        Ok(field) => println!("{:?}", field),
        Err(_) => println!("Field can not be parsed successfully"),
    });

Если вы хотите прикрепить это поведение к своему пользовательскому производному, вы можете использовать следующее с помощью цитаты ящика:

let name = &input.ident;

let output = quote! {
    impl #name {
        pub fn introspect(){
            input
            .fields
            .iter()
            .for_each(|field| match field.parse_named() {
                Ok(field) => println!("{:?}", field),
                Err(_) => println!("Field can not be parsed successfully"),
             });
        }
    }
};

// Return output TokenStream so your custom derive behavior will be attached.
TokenStream::from(output)

Поскольку поведение внедряется в вашу структуру как интроспективную функцию, вы можете вызывать ее в своем приложении следующим образом:

#[derive(Introspect)]
struct MyStruct {
    num: i32,
    text: String
}

MyStruct::introspect();

Примечание. Так как пример, который вы ищете, похож на этот вопрос. Этот Proc Macro Answer и декларативный макрос также должен дать вам представление

Ответ 2

Чтобы развернуть мой комментарий, вы можете использовать rustdoc, генератор документации Rust, чтобы просмотреть почти все, что вы просите (во время компиляции). rustdoc отобразит:

  • Структуры (включая публичные элементы и их блоки блокировки)
  • Перечисления
  • Черты характера
  • Функции
  • Любые комментарии к документации, написанные автором ящика с помощью /// или //!.

rustdoc также автоматически ссылается на источник каждого файла в ссылке [src].

Здесь приведен пример вывода rustdoc.

Стандартная библиотека

Ссылка на стандартную библиотеку API доступна здесь и доступна для чего-либо в пространстве имен std.

Crates

Вы можете получить документацию для любого ящика, доступного на crates.io на docs.rs. Это автоматически генерирует документацию для каждого ящика при каждом выпуске на crates.io.

Ваш проект

Вы можете создать документацию для своего проекта с помощью Cargo, например:

cargo doc

Это также автоматически создаст документацию для ваших зависимостей (но не стандартную библиотеку).

Ответ 4

У меня написано очень простой ящик, в котором используется процедурный макрос. Это дает вам доступ к информации о членах и некоторые простые сведения о struct/enum, которые вы используете. Информация о методах не может быть задана, потому что процедурные макросы просто не могут получить эту информацию, и, насколько я знаю, нет никаких методов, которые могут дать такую ​​информацию.

Ответ 5

Я использую что-то вроде этого:

println!("{:?}", variable); // struct, enum whatever

Если это большой тип, используйте версию #:

println!("{:#?}", variable); // struct, enum whatever

Ответ 6

Если вам нужны имена полей внутри вашей программы, то вам, вероятно, нужно использовать макросы. Либо оберните свое определение структуры в макрос и сопоставление с образцом, чтобы создать некоторую функцию для получения их имен, либо используйте процедурный макрос, чтобы получить структуры для признаков с такими функциями.

Смотрите примеры в syn для производных черт. В частности, смотрите syn :: Data :: Struct, который имеет поля.