I saw the following definition in a book:
pred show(b: Book){
some b.addr
}
where
abstract sig Name, Addr {}
sig Book { addr: Name lone -> lone Addr }
After playing with the Alloy analyzer, I realized this is the same as
pred show(){
some b:Book | some b.addr
}
I was curious what is the advantage of specifying Book as an argument, and not use the second approach using quantifiers?
Using or not using arguments to predicates is not an 'approach' it has different semantics. If you include some b in your predicate you can't use all b outside of it...
For example:
sig Addr {}
sig Book {
addr: Addr
}
pred show {
some b:Book | some b.addr
}
pred show'[b:Book] {
some b.addr
}
check { show }
// These are not possible without an argument to show'
check { all b:Book | show'[b] }
check { some b:Book | show'[b] }
check { no b:Book | show'[b] }
Related
I want to create a macro which on the received model generates structures with all its fields and implements trait Default, but faced such problem.
My macro struct_field! is recognized as a field, not a macro inside the model!.
Мaybe someone knows how to solve this problem
macro_rules! struct_field {
($fild_name:ident: string = $def_val:expr,) => {
pub $fild_name: Arc<RwLock<String>>
};
($fild_name:ident: string = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<RwLock<String>>
struct_field!($($fields)+)
};
($fild_name:ident: bool = $def_val:expr,) => {
pub enable_kernel_settings: Arc<AtomicBool>,
};
($fild_name:ident: bool = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<AtomicBool>,
struct_field!($($fields)+)
};
}
macro_rules! model {
($model:ident { $($inner_model:ident : $inner_model_name:ident { $($fields:tt)+ },)+ }) => {
pub struct $model {$(
$inner_model: $inner_model_name,
)+}
$(pub struct $inner_model_name {
struct_field!($($fields)+) // <-- error
})+
};
}
error:
error: expected `:`, found `!`
--> .../mod.rs:43:29
|
43 | struct_field!($($fields)+)
| ^ expected `:`
...
48 | / model! {
49 | | MainStruct {
50 | | inner_struct1: InnerStruct1 {
51 | | feild1: bool = true,
... |
58 | | }
59 | | }
| |_- in this macro invocation
example:
model! {
MainStruct {
inner_struct1: InnerStruct1 {
feild1: bool = true,
},
inner_struct2: InnerStruct2 {
feild1: bool = false,
feild2: bool = false,
feild3: string = "ignore",
},
}
}
expected result:
pub struct MainStruct {
inner_struct1: InnerStruct1,
inner_struct2: InnerStruct2,
}
pub struct InnerStruct1 {
feild1: Arc<AtomicBool>,
}
pub struct InnerStruct2 {
feild1: Arc<AtomicBool>,
feild2: Arc<AtomicBool>,
feild3: Arc<RwLock<String>>
}
you can test this with "cargo expand"
and you should be enabled rust-nightly
Contrary to C-like macros, Rust macros can only operate on valid AST nodes, that is, they can only be passed something that can be tokenized and, if needed, parsed to some extent, and can only "return" a valid AST node. This, in particular, rules out the invocation of a macro that would be extended as a field definition, because something like a: bool is not a valid AST node.
It is still possible to make a macro to do what you want to do, using function-style procedural macros, but it may become somehow more convoluted.
I'm trying to make a macro that would let me iterate through a list of types to reduce trait impl boilerplate. (I'm currently using a different macro based solution, but this seems more readable, if it's possible without adding a dependency.)
This is the syntax I am aiming for:
trait MyTrait {}
tfor! {
for Ty in [i32, u32] {
impl MyTrait for Ty {}
}
}
My attempt:
macro_rules! tfor {
(for $ty:ident in [$($typ:ident),*] $tt:tt) => {
$(
type $ty = $typ;
tfor! { #extract $tt }
)*
};
(#extract { $($tt:tt)* }) => {
$($tt)*
};
}
This generates an error as both the iterations define a type named Ty in the same scope:
|
4 | type $ty = $typ;
| ^^^^^^^^^^^^^^^^
| |
| `Ty` redefined here
| previous definition of the type `Ty` here
Playground
Is there a way around this? Can I somehow generate a random identifier in place of Ty so that this compiles, without using a proc macro or a dependency?
You can scope the trait implementation inside a const declaration. That way you can re-use the Ty name without causing conflicts.
macro_rules! tfor {
(for $ty:ident in [$($typ:ident),*] $tt:tt) => {
$(
const _: () = {
type $ty = $typ;
tfor! { #extract $tt }
};
)*
};
(#extract { $($tt:tt)* }) => {
$($tt)*
};
}
trait MyTrait {}
tfor! {
for Ty in [i32, u32] {
impl MyTrait for Ty {}
}
}
How do I persuade the Rust compiler that the internal match expression is fine here, as the outer match has already restricted the possible types?
enum Op {
LoadX,
LoadY,
Add,
}
fn test(o: Op) {
match o {
Op::LoadX | Op::LoadY => {
// do something common with them for code reuse:
print!("Loading ");
// do something specific to each case:
match o {
// now I know that `o` can only be LoadX | LoadY,
// but how to persuade the compiler?
Op::LoadX => print!("x"), /* LoadX specific */
Op::LoadY => print!("y"), /* LoadY specific */
_ => panic!("shouldn't happen!"),
}
println!("...");
}
Op::Add => println!("Adding"),
}
}
fn main() {
test(Op::LoadX);
test(Op::LoadY);
test(Op::Add);
}
I tried two approaches, but neither seems to work.
Name the or-pattern and then match using that name:
match o {
load#(Op::LoadX | Op::LoadY) => {
// ...
match load {
// ...
}
}
That's not valid Rust syntax.
Name and bind every constructor:
match o {
load#Op::LoadX | load#Op::LoadY => {
// ...
match load {
//...
}
}
That still doesn't satisfy the exhaustiveness check, hence the same error message:
error[E0004]: non-exhaustive patterns: `Add` not covered
--> src/main.rs:14:19
|
14 | match load {
| ^ pattern `Add` not covered
Is there any idiomatic way of solving this problem or should I just put panic!("shouldn't happen") all over the place or restructure the code?
Rust playground link
I think that you just need to refactor your code, obviously LoadX and LoadY are very close. So I think you should create a second enumeration that regroup them:
enum Op {
Load(State),
Add,
}
enum State {
X,
Y,
}
fn test(o: Op) {
match o {
Op::Load(state) => {
// do something common with them for code reuse
print!("Loading ");
// do something specific to each case:
match state {
State::X => print!("x"),
State::Y => print!("y"),
}
println!("...");
}
Op::Add => println!("Adding"),
}
}
fn main() {
test(Op::Load(State::X));
test(Op::Load(State::Y));
test(Op::Add);
}
This make more sense to me. I think this is a better way to express what you want.
You cannot. Conceptually, nothing prevents you from doing o = Op::Add between the outer match and the inner match. It's totally possible for the variant to change between the two matches.
I'd probably follow Stargateur's code, but if you didn't want to restructure your enum, remember that there are multiple techniques of abstraction in Rust. For example, functions are pretty good for reusing code, and closures (or traits) are good for customization of logic.
enum Op {
LoadX,
LoadY,
Add,
}
fn load<R>(f: impl FnOnce() -> R) {
print!("Loading ");
f();
println!("...");
}
fn test(o: Op) {
match o {
Op::LoadX => load(|| print!("x")),
Op::LoadY => load(|| print!("y")),
Op::Add => println!("Adding"),
}
}
fn main() {
test(Op::LoadX);
test(Op::LoadY);
test(Op::Add);
}
should I just put panic!("shouldn't happen")
You should use unreachable! instead of panic! as it's more semantically correct to the programmer.
I am trying to create a macro which initializes a struct T using P::Child from struct P where P: Parent<Child = T>.
macro_rules! init {
($t:tt, { $( $k:ident => $v:expr ),* }) => {
<$t as Parent>::Child {
$( $k: $v ),*
}
};
}
This macro passes the props as a map which is passed onto the struct's given constructor. Expanded, it would look like this:
#[derive(Debug)]
struct Apple {
a: i32
}
trait Parent {
type Child;
}
struct Mango;
impl Parent for Mango {
type Child = Apple;
}
fn main() {
let a = <Mango as Parent>::Child {
a: 4
};
println!("{:?}", a);
}
Compiling this has the error:
error: expected one of `.`, `::`, `;`, `?`, or an operator, found `{`
--> src/main.rs:25:38
|
25 | let a = <Mango as Parent>::Child {
| ^ expected one of `.`, `::`, `;`, `?`, or an operator here
I have created a macro to initialize a struct in a similar way but I am not able to do it with associated types. I think the compiler does not support it for some reason. Even so, I want to create a macro with such an API.
How could I approach this problem?
Link to Playground
Like so:
macro_rules! init {
($t:ty, { $( $k:ident => $v:expr ),* }) => {
{
type T = <$t as Parent>::Child;
T {
$( $k: $v ),*
}
}
};
}
My assumption would be that this is a limitation of the parser, not of the compiler's type analysis.
I don't think it is possible to initialize an associated type.
What you could do is perhaps have the Child: K with K being a trait having some known constructor (::new etc ...), Then call that constructor instead.
Alternatively you can probably initialize a T directly instead of Child.
I'm new to Rust, and I'm only just beginning to grasp some core concepts.
In my case, I need to return a vector of boxed traits. I'd like to do it in functional programming style, but I can't get it to compile.
Can someone explain why the below code works when I use a for loop (imperative style), but doesn't work when using a functional programming style using an iterator (filter_map in this case)?
Note: this is a contrived example and has been reduced to the simplest form I could write in order to demonstrate my misunderstanding.
#![allow(unused_variables)]
struct Foo {}
struct Bar {}
trait Thing {}
impl Thing for Foo {}
impl Thing for Bar {}
fn main() {
let things = get_things_using_for_loop(); // works!
let things = get_things_using_iterator(); // doesn't work! :-(
}
fn get_things_using_for_loop() -> Vec<Box<Thing>> {
let mut things: Vec<Box<Thing>> = vec![];
for t in ["foo".to_string(), "bar".to_string()].iter() {
if t == "foo" {
things.push(Box::new(Foo {}))
} else if t == "bar" {
things.push(Box::new(Bar {}))
}
}
things
}
fn get_things_using_iterator() -> Vec<Box<Thing>> {
["foo".to_string(), "bar".to_string()]
.iter()
.filter_map(|t| {
if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
}
})
.collect()
}
The function get_things_using_for_loop() compiles, while the function get_things_using_iterator() does not.
Here is the error:
error: mismatched types [--explain E0308]
--> <anon>:23:31
|>
23 |> Some(Box::new(Bar {}))
|> ^^^^^^ expected struct `Foo`, found struct `Bar`
note: expected type `Foo`
note: found type `Bar`
This has nothing to do with functional vs. imperative. Just pure types:
struct Foo {}
struct Bar {}
trait Thing {}
impl Thing for Foo {}
impl Thing for Bar {}
fn main() {
let t = "quux";
if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
};
}
What type is returned from the if/else? The first branch says it's an Option<Box<Foo>>. The second says it's Option<Box<Bar>>. Those aren't the same type. If you coerce the types:
if t == "foo" {
Some(Box::new(Foo {}) as Box<Thing>)
} else if t == "bar" {
Some(Box::new(Bar {}) as Box<Thing>)
} else {
None
};
It will all work.
You can also specify the type of the value:
let x: Option<Box<Thing>> = if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
};
Or, like you said, specify the type by annotating the closure:
.filter_map(|t| -> Option<Box<Thing>> {
This has actually nothing to do with those two programming styles, but is a mere coincidence of your code. In your first function you explicitly state the type of the vector:
let mut things: Vec<Box<Thing>> = vec![];
In your second function you never mention that you want a vector full of Box<Thing>. You only have an if-else construct, whose cases return:
Some(Box::new(Foo {}))
Some(Box::new(Bar {}))
None
The compiler sees the first case and assumes: "OK, the programmer wants to return Option<Box<Foo>>". But then in the next case you have an Option<Box<Bar>>. Now the compiler is confused. But it won't search the whole type space to find a trait that is implemented by both types (Foo and Bar). Also: there could be multiple traits which are all implemented by both, Foo and Bar. My point: you have to tell the compiler something about Thing!
To do so, you have multiple choices, for example:
if t == "foo" {
Some(Box::new(Foo {}) as Box<Thing>)
} else { /* ... */ }
Just adding as Box<Thing> to the first case is already enough and the compiler is happy.