-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
As reported in rust-lang/rust#15682 it is currently not allowed use the word self in a block passed to a macro because of hygiene issues.
struct A {
pub v: i64
}
macro_rules! pretty {
( $t:ty => $body:block ) => (
impl $t {
pub fn pretty(&self) -> String $body
}
)
}
pretty! (A => {
format!("<A:{}>", self.v)
});
fn main() {
println!("Pretty: {}", A { v: 3 }.pretty());
}
Proposal
I would like to propose to implement the workaround suggested by @Ryman and make an exception for the self keyword in the macro hygiene checker. Afterwards an issue/rfc can be made for a more complete solution.
Advantage
It will enable a class of macros that are currently unavailable. There are some workarounds but they make the macros less intuitive to use, for example the previous example could be written like this:
macro_rules! pretty {
( $t:ty, $_self:ident => $body:block ) => (
impl $t {
pub fn pretty(&$_self) -> String $body
}
)
}
And used like this:
pretty! (A, self => {
format!("<A:{}>", self.v)
});
This works but is unintuitive and gives a confusing error when used with another ident than self
.
Disadvantage
@huonw pointed out that this solution will break when an implementation is nested inside a method. This is valid of course, but to me it feels like a bit of a convoluted case. If it does happen in the real world I feel it would not likely fail silently, as both selfs would have to share an interface for the compilation to succeed.
Should this feature be blocked because it has no perfect solution right now? (A more complete suggestion was suggested by @Ryman but it looks to me like it would be a deep and complex hack)
Activity
durka commentedon May 3, 2016
This is a papercut that we should fix. +1.
petrochenkov commentedon May 4, 2016
It's not hygiene "issues", it's hygiene. In general, I don't like the idea of replacing the correct hygiene algorithm with some hacky hacks.
I'll try to poke around in resolve if I have time, but I expect issues. As a minimum, if
self
becomes unhygienic, it'll break code passingself
orself.something
to macros. You pass oneself
to a macro and it turns into anotherself
inside the macro.durka commentedon May 4, 2016
@petrochenkov another solution could be to allow other names for the self parameter in methods (maybe only when generated by macros?). The half-keyword status of
self
right now causes the following conundrum when you want to define a macro like @tinco's:self
, because hygiene otherwise prevents the twoself
s from being the same.self
, because the parser (unhygienically) checks that method receivers are namedself
.Besides not being discoverable from error messages, these two requirements combined make it seem like useless "Simon says" boilerplate.
tinco commentedon May 6, 2016
After working on my macros some more I think I agree with @petrochenkov now.
self
is just like any other method parameter in that it should be explicitly introduced by the macro so that it is hygienically captured.I think if Rust wants to have Ruby-like powerful DSL macro's there would have to be an 'unclean' directive and things like ident manipulation and stuff like that. I'm not familiar enough with Rust's philosophy to know whether that's desirable, but I'm thinking probably not.
@durka's point 2 still stands though, but I don't see how that could be solved unless Rust would simply allow any function that takes the right type as the first argument to be callable as a method. I bet that's already been discussed somewhere a long time ago when impl was designed and it's no doubt outside the scope of this issue.
arielb1 commentedon May 7, 2016
@petrochenkov
What code does this break?
self
is only bound by methods, and you can't useself
from outside of a method inside of it.Self
the type is already unhygienic, and I think that the 2 should be handled in the same way:petrochenkov commentedon May 7, 2016
@arielb1
Yeah, you are right, sorry. Everything I expected to break doesn't work already with "can't capture dynamic environment ..." errors.
From name resolution point of view
self
behaves exactly like other function parameter names andSelf
behaves like other type names, so it's the usual separation between hygienic local variables and unhygienic items. I expect the whole problem to go away eventually with @nrc's macros/syntax extensions 2.0 allowing unhygienic locals and hygienic items.ticki commentedon May 7, 2016
I feel like that this breaks hygiene. One should rather pass
self
to the macro, or in the glorious future,self.macro!()
. On the other hand, it seems convenient.self
unhygienic rust-lang/rust#33485petrochenkov commentedon May 7, 2016
Ok, I've submitted rust-lang/rust#33485, let's see what happens.
joelself commentedon May 24, 2016
Aaaaand it got closed.
joelself commentedon Jun 17, 2016
It appears that
self
has been made unhygienic, whether on purpose or on accident. I pulled the latest changes from master and deleted my changed code because it was completely out-of-date, but left in the test. I did aconfigure
,make rustc-stage1
and amake check-stage1
and lo and behold my unhygienic-self test passed!test [run-pass] run-pass/self-unhygienic.rs ... ok
I tried both rust stable:
and nightly:
You can see the code for the test here.
Edit: I still couldn't believe it myself so I found the built
rustc
an compiled the test and ran it. No errors on compile or run. I added aprintln!
to be sure:and sure enough:
I'm going to submit a pull request for the test to make the rust-lords decide if this is an intended feature or not.
Edit 2: Pull request submitted: #34317
jseyfried commentedon Jun 17, 2016
self
is still hygienic, see this comment.