[ANN] vec 0.1.0

I’m happy to announce the first release of vec, a library for safe dynamic arrays with Rust-like mutability permissions.

You can find the package on opam here, and the source repository is here.

Looking for feedback and suggestions!


I’m curious how you would compare this with Containers.Vector?


Oh, I didn’t know that existed.
Well, with vec you can control the vector’s growth rate (though I imagine this is a very niche feature).

Also, from what I can see CCVectors can only be read-write or read-only, vec also supports write-only vectors. (Which is useful if e.g. you want to pass a buffer to some function to fill it, but don’t want the function to read its current data)

1 Like

Super nitpicky here, but rust doesn’t have write-only either :slightly_smiling_face:

1 Like

Cool. I just worry that the Obj games needed for code like this are tricky, and so the collective review and testing that comes with focusing on fewer different libraries versus having a wide variety is very valuable. For example, be very careful with Obj.magic 0, it is an express elevator to segfault city. I don’t understand how one can safely avoid code like what CCVector does in fill_with_junk_ here in order to handle both flat float and general arrays safely. (And a drive-by comment, why is storing capacity necessary, isn’t it always possible to use the length of the backing array?)

I’m still wrapping my head around co- and contravariance.

Why is it important to define the main type

type ('a, -'p) t = (* ... *)


That is, why is the phantom type defined to be contravariant here?

From the type system point of view [R | W] means read or write, but in the context of this library it is used to mean read and write. So you have to interpret a disjunction as a conjunction, hence the contravariance annotation. This way you change the least supertype (disjunction) to the greatest subtype (conjunction).

True, it’s tricky to ensure you’re using it safely (case in point: I actually forgot to handle the float array case :sweat_smile:), but there’s no other way to create an “uninitialized” array.

Regarding capacity, reading it from the vector’s length means jumping through an extra pointer, and I thought it’s better to keep it inside the Vec.t record for better cache usage, given that it’s often checked together with other fields of the record (growth_rate and length).

1 Like

Basically what @kantian said.

It’s so that you can pass a ('a, [R | W]) Vec.t to a function expecting a ('a, [> R]) Vec.t.

Pvec from @dbuenzli (unreleased) wants to provide a vector without the required value to initiate it (and without Obj.magic). I don’t know the status of it but it’s quite interesting: https://github.com/dbuenzli/pvec


It’s a totally different thing, right? A persistent vector is quite different from an imperative vector. The latter should be super lightweight, and afaik there’s no good way of implementing it without Obj right now. There was a thread on this here last month or so.

1 Like