Replying to myself:
For homogenous vectors, you could also use the ffi (with c-lambda or ##c-code) and malloc for writing your own representation (should only be some 30-50 lines of code or so).
(Assuming you add a finalizer for free'ing the memory, of course.)
One issue to note is that the gc isn't aware of the big malloc'ed chunks, so doesn't "feel" the memory pressure if you're allocating many huge ffi vectors, so you're risking running out of memory soon. I wonder what the best solution to this problem is. One possibility would be to run ##gc once if malloc fails. That will trigger full gc's quite often (but honestly I'm still not clear whether Gambit employs a generational gc; movable objects are still movable after a gc (but there might be a hierarchy of movable object areas); so I don't know whether ##gc is more costly than a normal incremental collection). Another way could be some hook in the gc to inform it of the ffi memory.
(BTW the same issue arises when filehandles are running out because someone's not explicitely closing input ports; if filesystem calls are failing because of having run out of filehandles, a gc could be triggered and then the operation been retried once. This can have some importance when wrapping input ports as streams (lazy lists), because only a finalizer will close the port if a stream isn't exhausted (read to the end). Currently I'm ignoring this problem, thinking I could catch exceptions when opening and calling ##gc as mentioned above, but I wonder if it wouldn't be more efficient if there's some ##some-gc procedure which does clean up just the first generation, and maybe put that into the core instead of trapping exceptions in the library.)
Christian.