Looks like you don't understand some very important parts going under the hood.
1. HTTP GET request can't contain an empty string as a path.
When you type sub.example.com/ at your browser address bar, it is a browser who hides a trailing slash. This is applied only for the root requests, in any other case (including sub.example.com//, sub.example.com/test/, sub.example.com/test//, etc.) you'll see the full path at the address bar. No matter if you type that slash or not, HTTP request issued by the browser will look like
GET / HTTP/1.1
Host: sub.example.com
...
2. rewrite nginx directive works with the normalized URI.
Both location and rewrite directive works with so-called normalized URIs:
The matching is performed against a normalized URI, after decoding the text encoded in the “%XX” form, resolving references to relative path components “.” and “..”, and possible compression of two or more adjacent slashes into a single slash.
That means, for all the requests like sub.example.com/test/, sub.example.com/test//, sub.example.com/test///, etc., nginx see the normalized request URI as /test/ (that's the reason your rewrite rule works in a single step rather than the four-step sub.example.com/test/// -> sub.example.com/test// -> sub.example.com/test/ -> sub.example.com/test loop).
And the same is true for any of the sub.example.com/, sub.example.com//, sub.example.com///, etc. requests, the normalized URI will be seen by nginx as / making any rewrite rule unusable.
However, slashes compression can be turned off using the merge_slashes directive (read the security considerations). And to prevent multiply redirects where each redirect removes only a single trailing slash, use a non-greedy * and a greedy + quantifiers for your regex pattern:
merge_slashes off;
rewrite ^(/.*?)/+$ $1 permanent;