# Why variable defined in {% if ... %} invisible outside the macro?

**URL:** <https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737>\
**Category:** Help & Support\
**Created:** [February 11, 2025, 1:20pm UTC](https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737 "2025-02-11T13:20:36Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)\
**Post date:** [February 11, 2025, 1:20pm UTC](https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737/1 "2025-02-11T13:20:36Z")

</div>

When I tried to fix an issue in markd shards, I encountered a strange problem.

Following is the reduced code for reproduce.

```auto
require "uri"

class Foo
  def hello
    str = "你好"

    {% if compare_versions(Crystal::VERSION, "1.2.0") < 0 %}
      encoded_str = URI.encode(str)
    {% else %}
      encoded_str = URI.encode_path(str)
    {% end %}

    p encoded_str # not work, Error: undefined method 'encoded_str' for Foo
    p! encoded_str # "%E4%BD%A0%E5%A5%BD", it works
  end
end

foo = Foo.new
foo.hello

```

Why above code, `p! encoded_str ` work no error, but when `p encoded_str`, get `undefined method 'encoded_str' for Foo`?

Thanks

---

<div class="post-metadata">

**Author:** ![nobodywasishere](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/nobodywasishere/32/1983_2.png) [@nobodywasishere](https://forum.crystal-lang.org/u/nobodywasishere)\
**Post date:** [February 11, 2025, 2:28pm UTC](https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737/2 "2025-02-11T14:28:17Z")

</div>

Local variables have to be declared directly in the current scope, not inside any macros. Try:

```crystal
require "uri"

class Foo
  def hello
    str = "你好"

    encoded_str = {% if compare_versions(Crystal::VERSION, "1.2.0") < 0 %}
      URI.encode(str)
    {% else %}
      URI.encode_path(str)
    {% end %}

    p encoded_str # not work, Error: undefined method 'encoded_str' for Foo
    p! encoded_str # "%E4%BD%A0%E5%A5%BD", it works
  end
end

foo = Foo.new
foo.hello

```

---

<div class="post-metadata">

**Author:** ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)\
**Post date:** [February 11, 2025, 2:41pm UTC](https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737/3 "2025-02-11T14:41:15Z")

</div>

But, why `p! encoded_str` work? it not wrapped by {% … %} anyway.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 11, 2025, 2:44pm UTC](https://forum.crystal-lang.org/t/why-variable-defined-in-if-invisible-outside-the-macro/7737/4 "2025-02-11T14:44:19Z")

</div>

It’s likely that `p!` is a macro itself so maybe that changes something. Same as as why just wrapping your original `p encoded_str` line with `{% begin %} / {% end %}` works as well.
