This is tricky because a Vec<Vec<T>> handles a lot of this stuff at runtime.
If it's a blatant programming error (e.g. you document that one of your function's invariants is that it gets an even number of vectors) then it's perfectly fine to do an assertion and blow up loudly so the caller knows they need to fix their code.
Otherwise if these sorts of errors are expected (e.g. you're handling user input) then the typical solution is to return a Result<T, Error> and handle the error like you would any other.
Some other thoughts...
Instead of using a jagged array, can you store everything in one long Vec<T> and use index math to switch between rows? That way you'd be able to guarantee each row has the same length.
Depending on how much code uses this vector of vectors, you may also want to pull it out into its own type. That way the inputs can be checked once during construction and everything can assume it's always valid. That's typically how you'd implement something like a ChessBoard.
You can create an intermediate type which represents the unvalidated form of the type you want to ultimately construct. Never construct the type directly, but rather instead via the validations.
Of course, I can store anything in a unique array, but this would be very efficient for me and the function itself but it would feel strange or hard to understand for the final user of the function (the developer who uses the function).
Your final thought is maybe what I was looking for, but I don't know how to do it: that is, build a new type extending the outer vector, so that its inner vectors are i32 vectors, at least 2, same size, etc. At that point, I simply pass one argument of this function, with this custom type. But eventually at the moment I don't know where to start
Here's an example you might use when creating a 2D matrix type. It's probably not exactly what you need for your function, but it may give you an idea as to how it can be implemented.
The _ in a type name asks the compiler to fill in the correct type using type inference. Function signatures deliberately don't participate in type inference for many good reasons (it means you can no longer just look at a function's signature to know what it does, promotes non-local reasoning, etc.).
Ah, yes, I should have called that out. I was providing a sketch where one could fill in any type.
This is probably a better example overall of what I was talking about: Rust Playground
It's essentially the same idea as @Michael-F-Bryan posted. One difference is that I'm specifically emphasizing the use of a wrapper type ValidatedVec, which exists only as a way of documenting and proving that a vector is validated when you use it.
That means any time you have a function which accepts a ValidatedVec as an argument, you know that it has the properties you defined without having to perform assertions again. If you have one function at the top of a call tree which validates the vector once, the functions down the tree don't have to check for themselves.
The type system here isn't guaranteeing that you can only construct a ValidatedVec using this function I wrote out, so it's not actually guaranteed to be correct as I've written it. But it does lessen the error surface a bit. It can increase your confidence in the code enough to not validate the input vector in every single function which receives it.
Use cases may vary, but I like this pattern a lot.