Should we avoid assignment of derived types in robust programs?

One possible way of getting a non-assignable object while using a form of RAII, is to use associate.

First you need to overload the structure constructor:

  function new_ext_t(val) result(this)
    integer, intent(in), optional :: val
    type(ext_t) :: this

    if (present(val)) then
      this%val = val
    end if
  end function

Next you build a facade module that only exposes the overloaded structure constructor, but keeps the type hidden:

module test_facade
  use test, only: ext_t => new_ext_t
end module

Finally, you instruct consumers to use the container within an associate block:

program testprog
  use test_facade, only: ext_t
  implicit none

  type(ext_t) :: a  ! compile-time error

  associate(ext => ext_t(3))
    ! ... access public members of ext ...
    ext = ext_t(4)  ! compile-time error
  end associate

Inside the associate statement I believe you can still call any type-bound methods (not sure what happens if a method is intent(out)). The objects is also supposed to be finalized upon exiting the associate block (see Should associate trigger automatic finalization?). Unfortunately, you may bump into issues with compiler support.

Their is a nice example of this pattern in the nlopt-f library for defining callbacks that get passed to a calling C routine.

Edit: If you need a copy, you can overload the structure constructor with a second method,

module test_facade

  use test, only: new_ext_t, copy_ext_t
  private

  public :: ext_t

  interface ext_t
    procedure :: new_ext_t, copy_ext_t
  end interface

end module

that can be used with a second associate statement:

  associate(this => ext_t(3))
    ! ...
    associate(copy => ext_t(this))
      ! ...
    end associate
  end associate
1 Like