14

Let's take this simple function:

  @spec laBionda(String.t()) :: String.t()
  def laBionda(name \\ "you") when is_bitstring(name) do
    "One for #{name}, one for me"
  end

I only want to define the function for String inputs.

Should I use is_bitstring or is_binary on the Guard? Are there any differences? Both seem to be fine in that case.

Marvin Rabe
  • 4,141
  • 3
  • 25
  • 43
  • 2
    A bitstring is a type that stores arbitrary number of bits, you can have a 3bit bitstring whereas binary stores arbitrary number of bytes. A binary is just a collection of bytes, so it has to have a number of bits that is divisible by 8. So to avoid confusion best to use `is_binary` for your ascii strings. See also here https://elixirforum.com/t/bitstring-and-binary/2351#post_2 – GavinBrelstaff Jan 12 '19 at 16:08
  • There is a great talk about strings that explains the differences: https://www.youtube.com/watch?v=zZxBL-lV9uA – Marvin Rabe Jan 12 '19 at 16:51

2 Answers2

22

You should use is_binary/1.

Strings in Elixir are represented as binaries. Elixir binaries are sequences of bytes, whereas bitstrings are sequences of bits. While all binaries are bitstrings, not all bitstrings are binaries.

is_bitstring/1 can return true for some bitstrings that cannot be represented as a binary, for example, the single bit:

iex(1)> is_binary(<<1::1>>)
false
iex(2)> is_bitstring(<<1::1>>)
true

You expect strings only. Bitstrings that are not binaries are never desired, so the more specific is_binary/1 is the better choice.

Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74
6

According to this answer: https://stackoverflow.com/a/10821192/10895259

The difference between in a binary and a bitstring is that the length of a binary is evenly divisible by 8, i.e. it contains no 'partial' bytes; a bitstring has no such restriction.

The example from guides demonstrates this difference.

iex> is_binary(<<100 :: size(9)>>)
false
iex> is_binary(<<100 :: size(8)>>)
true
iex> is_binary(<<100 :: size(7)>>)
false

In your function, it does not matter what guard to use. But is_binary/1 is a convention, as the strings are stored in bytes.

  • 2
    It does matter which will be used there, as `is_bitstring/1` will pass invalid "Elixir strings", for example `"foo #{<<100 :: size(7)>>} bar"` will fail as `String.Chars` is not implemented for bit strings. – Hauleth Jan 14 '19 at 16:43