A long time ago a friend of mine pointed out a sign of trouble in any program: defining a new container model. If you are working in a language that provides containers in the standard library, then use them. If you need a container with specific behaviour, then make it look like the standard containers. If you come across a program which introduces a new model, beware.
I’ve recently been adding Go support to SWIG. SWIG can be used to build interfaces between various different languages and C++. SWIG is a nice program in many ways: it provides a lot of power and flexibility in defining the interface. Internally, though, big chunks are written in C++ but it uses a totally different container model. I assume this is for historical reasons dating back to a beginning in C or possibly even Python. In C++, though, it’s a mess. There is no type checking: it’s all
void * pointers. Practically every type (strings, lists, hash tables) converts to everything else implicitly. It brings all the weaknesses of a pure dynamically typed language into C++, without providing any of the benefits.
Sometimes one encounters a programmer who carries a container model from program to program, rewriting it into different languages as needed. The result is a program written for one person. Don’t do that. Use a language in the idioms of the language; don’t try to make it look like a different language. Use the containers which the language provides.