Crashing in `protocol witness for Collection.distance(from:to:) in conformance Metadata.Dictionary`: now what?

So I'm trying to make something a Collection. What's slightly odd here is that my Iterator is not a struct, but a class, and in fact, it's derived off a class defined as a subclass of NSObject and implemented completely in Objective-C++. The reason for this is the Iterator needs to hold a C++ std::map<> iterator.

When I call "map" on my collection in swift, it crashes quickly, in I assume a default implementation function needed to run map (specifically, according to Xcode, in protocol witness for Collection.distance(from:to:) in conformance Metadata.Dictionary. (My collection is named Metadata.Dictionary.)

What's funny is that I can do a

           for (key, value) in collection {
             ...
          }

loop just fine, so I thought I had my iterators correct. But as soon as I write

      collection.map {
          <simple code>
      }

I crash even before I get into the body the first time.

I've put print statements in all the relevant functions of my iterator to make sure nothing funny happens, and basically, map gets an iterator to the beginning, an iterator to the end, jumps into the routine in the subject line, and crashes hard.

Anybody got a suggestion on how I got about debugging this?

I assume that there's no rule that says an iterator must be a struct, and making it a class is OK. (I don't like this, but I can't think of any other good way to hold a std::map<> iterator. Suggestions as to how to manage this without making the Iterator a class are greatly appreciated.)

Is it possible for you to post the relevant parts of the code? It's hard to tell what might be wrong based just on the verbal description.

I'd also recommend sticking with Swift for your Iterator type, by declaring a struct and having it wrap the Obj-C++ type, rather than trying to use that directly as the iterator itself. Iterators are pretty trivial – just an init and next – so it should be trivial to just forward on to the aggregated type.

There's no rule, but using a reference type for an iterator fills me with a general sense of unease (even though iterators are "reference-y", it's easy to invalidly assume they're value types that "branch" iteration when you copy them).

1 Like

Oh, duh, i can stick the objective-C++ type inside the struct, and it'll be properly reference counted around. I think I will refactor to do this immediately, because looking at the inout-ness of the default methods (I found some code on GitHub) also made me very uneasy I was using a class, not a struct.

Let me see if that fixes it before I go any further. Thanks.

So both my Index type and my Iterator were deriving off an NSObject-derived objective-C++ class. I shifted it so that instead, Index and Iterator became structs that simply contained an instance of the NSObject-derived class.

This turned out not to be the problem, though I think using structs is still much better.

The issue turned out to be that I thought that Comparable only required a "<" function; in fact it requires "<" and "==" and the compiler was (silently) synthesizing an "==" function that involved the pointers to the objective-C++ class, and which was obviously completely bogus.

So a complete red-herring --- just ordinary programmer stupidness.

4 Likes