HentaiMew
HentaiMew

Reputation: 337

Pattern matching on binary type cannot use the value of a variable

E.g:

arg = "echo:hello"
prefix = "echo"

case arg do
  <<^prefix, ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
  _ -> IO.puts("No match")
end

Result: No match

What if I want to use the value of prefix as a pattern match?

Upvotes: 5

Views: 439

Answers (1)

Jonas Dellinger
Jonas Dellinger

Reputation: 1364

This won't work because you can only match fixed size binaries if you're not matching the "rest" of the string. There are 3 different solutions, depending on your use-case

1. Calculate the size beforehand

If you really want to use that binary pattern matching, you can manually calculate the size beforehand:

arg = "echo:hello"
prefix = "echo"
prefix_size = byte_size(prefix)

case arg do
  <<^prefix::binary-size(prefix_size), ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
  _ -> IO.puts("No match")
end

2. Use compile-time module attributes

Depending on your use case, you could use module attributes, which size is known at compile time so they do work:

defmodule MyModule do

  @prefix "echo"

  def test(arg) do
    case arg do
      <<@prefix::binary, ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
      _ -> IO.puts("No match")
    end
  end
end

3. Use String.replace_prefix/3

Or, if you want to keep prefix a runtime binary, you could use String.replace_prefix/3

arg = "echo:hello"
prefix = "echo"

case String.replace_prefix(arg, "#{prefix}:", "")do
  ^arg -> IO.puts("No match")
  msg -> IO.puts("Echo match: #{msg}") 
end

String.replace_prefix/3 returns the input string if there is no match found, so we match it via ^arg. If this is not the case, we got a match and since we replaced the prefix with "", we just get the part after the :.

Upvotes: 8

Related Questions