Skip to content

Array: clear and release backing storage without a placeholder #4205

Description

@Yu-zh

Is your feature request related to a problem? Please describe.

Array::clear() makes an array logically empty, but on non-JavaScript backends it intentionally retains the backing buffer and the references stored in its slots. This is useful when the capacity will be reused, but it is unsuitable when those references must be severed explicitly to break a cycle.

One motivating case is a coroutine or scheduler state that owns an array-backed queue of callbacks/closures. The queued callbacks may capture the coroutine state, producing a cycle like:

coroutine state -> callback queue -> callback closure -> coroutine state

When the coroutine is completely finished, emptying and detaching the queue can break this cycle and make the objects eligible for reclamation.

Array::release_unused(placeholder=...) can overwrite the retained slots after clear(), but it requires a value of T. For callback/function types and other reference-bearing types, there may be no canonical harmless default value. It also fills the entire unused capacity and retains the allocation.

Describe the solution you'd like

Add an operation that empties an array and detaches its backing storage without requiring a placeholder. Tentatively, this could be named Array::reset:

pub fn[T] Array::reset(self : Array[T]) -> Unit {
  self.len = 0
  self.buf = []
}

The intended contract is:

  • length() is zero afterwards.
  • The backing storage is relinquished (capacity() becomes zero where capacity is meaningful).
  • References held by both the live prefix and unused capacity are no longer reachable through the array, allowing cycles to be broken.
  • No T, Default bound, or placeholder is required.
  • The array remains valid and can be reused by pushing new elements.
  • The API is available consistently across backends; on JavaScript, truncating the underlying array to length zero is sufficient.

Replacing the buffer directly also avoids explicitly overwriting every retained slot as release_unused does. Actual reclamation remains runtime-dependent, and an outstanding ArrayView may keep the previous buffer reachable subject to the existing view-lifetime rules.

The name is open for discussion. Since reset on some existing buffer-like types means “become empty while retaining capacity,” a more explicit name such as clear_and_release or clear_and_shrink may communicate the storage behavior better.

Describe alternatives you've considered

  • array.clear(); array.release_unused(placeholder=...) breaks references but requires a suitable placeholder, scans the unused capacity, and retains the allocation.
  • array.clear(); array.shrink_to_fit() is semantically close and does not need a placeholder. A dedicated operation would express the cycle-breaking intent as one API operation and could use the direct buffer-detachment implementation where supported.
  • Letting the array itself become unreachable is sufficient when possible, but not when it belongs to long-lived state or participates in the cycle that needs to be broken.

Additional context

This operation is intended for the end of an array's logical use, especially resource-management and coroutine/scheduler cleanup paths. It is not intended to replace clear() in reuse-heavy code where retaining capacity is desirable.

  • I do think that this should be part of the core library and should be delivered to all the MoonBit developers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions