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)))
end

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

But never uses Obj :wink:

1 Like

You could list just one of the fields

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

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

Cheers,
Nicolas

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

Is this doing a deep copy of the struct?
I.e. all arrays are copied to freshly allocated ones?

Obj.dup always performs a shallow copy. If you want a deep copy you can use Marshal to serialise and de-serialise, but I would advise to actually write the copy function yourself. It’s often not that much work and you can keep the sharing on the immutable parts (or the mutable parts you don’t need to duplicate).

2 Likes

This seems like a job for a ppx.

I just did as you suggest.
The problem is that if I forget something important in this copy function, then there will be a bug.