function to get the raw representation of a struct

What Elixir function is used to get the raw representation and inspect the internal structure of a struct? (I know that is possible as shown by the i/1 function)

More importantly, how do I browse through the Elixir codebase to answer the above question myself?

I’ve tried inspect/1 but it gives me a string representation of the timestamp without showing any fields.

iex()> utc = DateTime.utc_now
iex()> i utc
Term
  ~U[2025-06-24 11:35:03.866819Z]
:
Raw representation
  %DateTime{year: 2025, month: 6, day: 24, hour: 11, minute: 35, second: 3, time_zone: "Etc/UTC", zone_abbr: "UTC", utc_offset: 0, std_offset: 0, microsecond: {866819, 6}, calendar: Calendar.ISO}
iex()> inspect(utc)
"~U[2025-06-24 11:35:03.866819Z]"

Finally, is this the right place for these sort of questions?

1 Like
iex(1)> inspect(DateTime.utc_now(), structs: false)
%{__struct__: DateTime, calendar: Calendar.ISO, day: 24, hour: 13, 
  microsecond: {86235, 6}, minute: 42, month: 6, second: 7, std_offset: 0, 
  time_zone: "Etc/UTC", utc_offset: 0, year: 2025, zone_abbr: "UTC"}

it gives me a string representation

Technically, it’s not a string representation. It’s a sigil which Elixir uses to build the datetime from its string representation.

3 Likes

To answer your initial question:
You can use the :structs option for IO.inspect/2, e.g. IO.inspect(~U[2025-06-24 11:35:03.866819Z], structs: false). The IO.inspect/2 docs will link to the Inspect.Opts docs, which has the rest of the options that can be used. The Inspect protocol is what would be used to customize the output. Structs use a default implementation that is basically the same as the Map, but hides the __struct__ key.

In general, when looking at the docs, the typical thing you’re looking for is a struct type definition. You’ll typically see t() for structs on a module (Date.t() for example)

As for “is this the right place”, this is fine. Typically you’ll get a faster response on the Slack or Discord though (check the Elixir-lang site here for the links)

1 Like

Interesting.

Both i and inspect are using protocols.

As the others said, you can customise inspect to log the underlying map (including the __struct__ key) by passing the structs: false option. I don’t think there’s a built-in way to log an equivalent to the raw representation section from i.


Here follows TMI about protocol implementations :rabbit_face::hole:

Elixir includes a default Inspect implementation to log structs that have not implemented the Inspect protocol:

iex(1)> defmodule Example, do: defstruct [:foo]
{:module, Example, <<...>>, %Example{foo: nil}}
iex(2)> %Example{}
%Example{foo: nil} # <-- default implementation

But DateTime has its own implementation of the Inspect protocol that serialises it as a sigil:

iex(3)> DateTime.utc_now
~U[2025-06-24 14:01:48.773039Z] # <-- DateTime's implementation

i is actually overriding any type-specific Inspect implementations to dump the raw representation using the default Inspect.Any implementation.

Here’s what IEx.Info is doing internally, vs. what happens when you call inspect normally on a DateTime. Note the use of Inspect.Any, which is the default implementation IEx.Info calls explicitly, vs. Inspect.DateTime, which is what you get when you pass a DateTime struct to inspect.

iex(4)> DateTime.utc_now |> Inspect.Any.inspect(%Inspect.Opts{}) |> Inspect.Algebra.format(:infinity) |> IO.iodata_to_binary
"%DateTime{year: 2025, month: 6, day: 24, hour: 14, minute: 7, second: 18, time_zone: \"Etc/UTC\", zone_abbr: \"UTC\", utc_offset: 0, std_offset: 0, microsecond: {379886, 6}, calendar: Calendar.ISO}"
iex(5)> DateTime.utc_now |> Inspect.DateTime.inspect(%Inspect.Opts{}) |> Inspect.Algebra.format(:infinity) |> IO.iodata_to_binary
"~U[2025-06-24 14:07:23.538947Z]"
2 Likes