Why does my random generation of strings fails?

Let me preface this by saying that I know I can fix my problem by using a library but this is just me doing some elixir exercises for fun. Ok, here is my problem, I want to model a graph and so I created a Vertex struct, this struct has an id property and I want it to be self generating random unique string.

This is my code:

defmodule Vertex do
  defstruct value: nil,
            id: Base.encode16(:crypto.strong_rand_bytes(64)),
            neighbours: MapSet.new([]),

  def new(value), do: %Vertex{value: value}

  def add_neighbour(
      ... Some code here
  end


The unique id generation code is something I copy pasted from a elixir course I took. The problem is that when I try to create vertices I get the same string every time

iex(1)> %Vertex{value: "Bryan"}
%Vertex{
  value: "Bryan",
  id: "59832511CE7A95FFA4BC22FD5A28581DD40AA20C93ACC3E6BAB9B178F6C8D11F3AF14A5EB047846384D5B5CC4B64609C4ABCFFC6D9C4260AA3551A97899253D5",
  neighbours: MapSet.new([]),
}
iex(2)> %Vertex{value: "Cris"}
%Vertex{
  value: "Cris",
  id: "59832511CE7A95FFA4BC22FD5A28581DD40AA20C93ACC3E6BAB9B178F6C8D11F3AF14A5EB047846384D5B5CC4B64609C4ABCFFC6D9C4260AA3551A97899253D5",
  neighbours: MapSet.new([]),
}

But when I execute just the function call it works:

iex(3)> Base.encode16(:crypto.strong_rand_bytes(64))
"16D7500A3A7629E7D83D65E35D3B49CF4D5EB55D3464EFBC44753F268D6C0457C4EC366C7F27CF7BC72A99F8FC7E407BAA9D28E4C39B1F97AB1083174D3C4C1A"
iex(4)> Base.encode16(:crypto.strong_rand_bytes(64))
"D950A48722542628849FD7A0097DE9E7324C4426F6697FEB1A2C35968981FE0B218148CB281BA08B4CEEAC5F3DABAC2C129CF258E466E6FCBF699B2CAA9C7076"

Why is this happening ? It seems like seeding problem but I want someone to explain it to me please.

Because the function calls here are being run at compile time, not runtime. In essence, you’re setting the default to the result of the function call (a call made once at compile time), not saying run the function to generate a default when a new instance of the struct is made at runtime.

Since you have a new/1 function anyway… you could do something like this:

def new(value) do 
  %Vertex{
    id: Base.encode16(:crypto.strong_rand_bytes(64)), 
    value: value}
end

That would be runtime generation.

2 Likes

Yeah, that was it. Thanks a lot

1 Like