Сопоставление с типом возврата вмещающей функции - PullRequest
0 голосов
/ 24 января 2019

Возможно ли для макроса Rust совпадать с типом возвращаемого значения включающей функции?

Примером может служить что-то вроде макроса logging и assert, который также возвращает Err в функциях, возвращающих Result, и вызывает паникуфункции не возвращаются Result.Для реализации этого макрос должен каким-то образом знать о типе возвращаемого значения включающей функции.

Я полагаю, что это невозможно с декларативными макросами (macro_rules!), поскольку они имеют ограниченный набор совпадающих типов (как описано в Справочник по Rust, глава «Макросы по примерам» ): элементы, блоки, операторы, шаблоны, выражения, типы, идентификаторы и т. Д., Но не возвращаемый тип включающей функции.

Но, возможно, с процедурныммакросы?

1 Ответ

0 голосов
/ 24 января 2019

Резюме : Нет, это нелегко сделать даже с процедурными макросами.И я на самом деле думаю, что вы не должны писать такие вещи, даже если это возможно.Просто дайте вашему макросу вычислить значение Result и пусть пользователь справится с ним.


Функциональный макрос

Макросы ржавчины, процедурные и декларативные, имеют доступ только к своему вводуПоток: просто список токенов.Для функционально-подобных макросов (те, которые вы вызываете через foo!(...)), ввод - это то, что вы им передаете.Таким образом, вы могли бы вручную передать тип возвращаемого значения:

macro_rules! foo {
    (Result $($stuff:tt)*) => { return Err(()); };  
    ($($stuff:tt)*) => { panic!(); };
}

fn returns_result() -> Result<String, ()> {
    foo!(Result<(), ()>);   // will return `Err`
    Ok("hi".into())
}

fn returns_string() -> String {
    foo!(String);           // will panic
    "hi".into()
}

Но я предполагаю, что это не то, что вам нужно: пользователь должен будет вручную указать тип возвращаемого значения для каждого вызова макроса.

То же самое касается процедурных макросов, которые вызываются таким образом.


Атрибут процедурного макроса

Можем ли мы определить процедурный макрос, в котором тип возвращаемого значения функции находится впоток входных токенов?Да, предпочтительно через атрибут proc-macro.Если вы определите такой атрибут bar, вы можете написать это:

#[bar]
fn returns_result() -> Result<String, ()> { ... }

И ваш процедурный макрос получит в качестве входных данных определение всей функции, включая тип возвращаемого значения. Но что вы собираетесь делать с этой информацией?

Вы можете изменить всю функцию так, как вам нравится, поэтому одной идеей будет поиск всех foo!() вызовов макросов в функции и замена их на return Err или panic!() в зависимости от типа возвращаемого значения.То есть: выполните шаг вызова макроса для своих собственных макросов через процедурный макрос.

Но Я думаю, что это плохая идея по нескольким причинам.Самое главное, я не думаю, что это хорошо определено, когда компилятор вызывает процедурный макрос.Таким образом, компилятор может попытаться вызвать ваши макросы foo!() перед вызовом процедурного макроса.

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


Что я считаю лучшим решением

Наконец, как бы я это сделал? Пусть ваш макрос оценивается в Result.Тогда пользователь может легко решить, что с ним делать.Если они возвращают Result, им просто нужно добавить ?.Если они этого не делают, у них есть свобода выбора между .unwrap(), expect() и другими способами паники.

Я понимаю, почему вы пытаетесь делать то, что вы хотите сделать (это легче дляПользователь и удобный), но я думаю, что это не очень хорошая идея.Вероятно, все сводится к «жуткому действию на расстоянии» : то, что макрос в вашей функции внезапно зависит от типа возвращаемого значения этой функции.Это означает, что когда вы меняете это, меняется вся семантика функции.Это звучит как то, с чем можно легко выстрелить себе в ногу.Это также, вероятно, причина, почему это не так просто в Rust.

...