How to create a copy of a record type with mutable fields

I’m creating a terminal_io struct which has a large number of mutable fields. Before modifying them, I want to create a copy of the struct in order to be able to restore it later. Currently I’m making a duplicate call to Unix.tcgetattr:

    let savedTio = Unix.tcgetattr Unix.stdin in
    let tio = Unix.tcgetattr Unix.stdin in

I know you can create a copy with modified variables e.g. with:

    let tio = Unix.tcgetattr Unix.stdin in
    let savedTio = { tio with ... = ... } in

But I don’t want to modify any values.

Is there a better way to do this?

In that case you’ll have to list all fields.

let clone_terminal_io { c_ignbrk; c_brkint; (* list all fields *) } =
  { c_ignbrk; c_brkint; (* ... *) }

You’ll have to write this only once and it will inform you in the unlikely case a field is added.

Thanks for the reply @emillon!

I thought that might be an option, but I was trying to avoid it as there’s 38 fields in this struct. I think I’ll stick with the existing approach of two syscalls.

I know that Obj module always triggers all the possibles warnings, but would Obj.dup will work in this case ?

It’s indeed possible:

module Make (X : sig type t end) : sig
  val copy : X.t -> X.t
end = struct
  let copy x : X.t = Obj.(obj (dup (repr x)))

let copy_my_struct =
  let module M = Make (struct type t = my_struct end) in

But never uses Obj :wink:

You could list just one of the fields

  (* make a copy *)
  let savedTio = { tio with some_field = tio.some_field } in

Why not modify the fields using the {... with ...} construction? This will make a copy at the same time.


You don’t need to list all the fields, it suffices to just copy one:

let clone_terminal_io ({c_ignbrk} as tio) = { tio with c_ignbrk }

Edit: @SkySkimmer already pointed this out

Right, I was thinking that the fields could alias but that’s not possible.

Thank you all for your replies.

This is exactly what I should be doing! I’ve implemented it here better tio copy · RyanGibb/ocaml-exec-shell@fdc85ef · GitHub