Примечание редактора. Этот ответ относится к версии Rust до 1.0 и содержит код, синтаксически недопустимый в Rust 1.0.
Если все, что вам нужно, это способ определенияapply
, попробуйте расширения синтаксиса Rust для каждого примера:
fn main() {
#macro[[#apply[f, [x, ...]], f(x, ...)]];
fn add(a: int, b: int) -> int { a + b }
assert (#apply[add, [1, 15]] == 16);
}
Приведенный выше код взят из набора тестов Rust .
К сожалению, документация поРасширения синтаксиса на данный момент немного редки.Справочное руководство Rust , вероятно, является лучшим выбором, хотя приведенный пример (apply
, не менее!) Устарел, поэтому я не уверен, насколько достоверной его информации можно доверять.
Обновление:
Осталось только выяснить, как обернуть add ... assert в функцию с правильной сигнатурой типа, которая принимает произвольные генераторы.
Я до сих пор не уверен, как именно вы все это делаете, но вот функция, которая принимает любую функцию, которая производит int
:
use std;
import std::rand;
fn assert_even(num_gen: fn() -> int) -> (bool, int) {
let num = num_gen();
ret (num % 2 == 0, num);
}
fn main() {
let rng = rand::mk_rng();
let gen_even = {|| (rng.next() as int) * 2};
log(error, assert_even(gen_even));
}
Однакоработа с числами в Rust в настоящий момент является болезненной задачей, и если вы хотите обобщить assert_even
для любого числового типа, вам придется определить интерфейсы / реализации, а затем объявить assert_even
с ограниченным универсальным типом:
use std;
import std::rand;
iface is_even { fn is_even() -> bool; }
impl of is_even for int { fn is_even() -> bool { self % 2 == 0 } }
impl of is_even for u32 { fn is_even() -> bool { self % 2u == 0u } }
fn assert_even<T: is_even>(num_gen: fn() -> T) -> (bool, T) {
let num = num_gen();
ret (num.is_even(), num);
}
fn main() {
let rng = rand::mk_rng();
let gen_even_int = {|| (rng.next() as int) * 2};
let gen_even_u32 = {|| rng.next() * 2u};
log(error, assert_even(gen_even_int));
log(error, assert_even(gen_even_u32));
}
Примечание: если вы заинтересованы в тестировании, вам следует проверить возможности Rust.См. руководство по Rust здесь для примера того, что делает typeState и каким образом оно способно обеспечить корректность программы.Насколько я понимаю, это в основном более мощная версия дизайна от Eiffel по контракту .
Обновление 2:
for_all принимаетодиночное свойство (например, is_even или divisible_by) и набор функций генератора.Генераторы - это лямбда-выражения, которые возвращают случайные значения для передачи в качестве входных данных в свойство, например, [gen_int] для is_even или [gen_int, gen_int] для divisible_by.for_all будет вызывать свойство, используя сгенерированные значения в качестве тестового примера, печатая +++ OK, прошло 100 тестов, если свойство возвращает true для 100 случайных тестовых случаев, или *** Failed!{test_case}, если один из тестовых примеров не пройден.
Этот полный исходный файл должен полностью демонстрировать поведение, которое вы ищете, надеюсь (определение for_all
находится в самом низу):
use std;
import std::rand;
import std::io::println;
iface is_even { fn is_even() -> bool; }
impl of is_even for int { fn is_even() -> bool { self % 2 == 0 } }
fn main() {
let rng = rand::mk_rng();
// Cast to int here because u32 is lame
let gen_even = {|| (rng.next() as int) * 2};
let gen_float = {|| rng.next_float()};
// Accepts generators that produce types that implement the is_even iface
fn assert_even<T: is_even>(num_gen: fn() -> T) -> bool {
let num = num_gen();
let prop_holds = num.is_even();
if !prop_holds {
println(#fmt("Failure: %? is not even", num));
}
ret prop_holds;
}
fn assert_divisible(num_gen1: fn() -> float,
num_gen2: fn() -> float) -> bool {
let dividend = num_gen1(),
divisor = num_gen2();
let prop_holds = dividend / divisor == 0f;
if !prop_holds {
println(#fmt("Failure: %? are not divisible", (dividend, divisor)));
}
ret prop_holds;
}
// Begin anonymous closure here
#macro[[#for_all[prop, [gen, ...]], {||
let passed_tests = 0;
let prop_holds = true;
// Nice iterators and break/continue are still being implemented,
// so this loop is a bit crude.
while passed_tests < 100 && prop_holds {
prop_holds = prop(gen, ...);
if prop_holds { passed_tests += 1; }
}
println(#fmt("Tests passed: %d", passed_tests));
ret 0; // Necessary to infer type of #for_all, might be a compiler bug
}()]]; // Close anonymous closure and self-execute, then close #macro
#for_all[assert_even, [gen_even]];
#for_all[assert_divisible, [gen_float, gen_float]];
}
Еще одна вещь: механизм расширения синтаксиса все еще довольно неполированный, поэтому невозможно импортировать макросы из разных ящиков.До этого момента определение #for_all
должно появиться в файле, в котором оно вызывается.