6

Swift, Can't compile, compiler will directly report error.

protocol Test {}

struct Test {}


// Swift compile output:

// Untitled.swift:4:8: error: invalid redeclaration of 'Test' struct Test {}

// Untitled.swift:2:10: note: 'Test' previously declared here protocol Test {}

Objective-C, Can be compiled successfully, for example NSObject is a class name, it is also a protocol name

#import <Foundation/Foundation.h>

@protocol Test
@end

@interface Test
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSLog(@"Hello word");
    }
}
// Objective-C output
// 2018-03-11 23:14:20.341 Untitled[34921:1272761] Hello word
TBXark VFanx
  • 206
  • 2
  • 10
  • `NSObject` being both a class and a protocol is confusing as hell. I'm not surprised they intentionally set out to stop this from happening again in Swift – Alexander Mar 11 '18 at 15:22
  • Possible duplicate of [What is Protocol Oriented Programming in Swift? What added value does it bring?](https://stackoverflow.com/questions/37530346/what-is-protocol-oriented-programming-in-swift-what-added-value-does-it-bring) – alex Mar 11 '18 at 15:58
  • 2
    @alex: That is a completely different question. The only common part is the word "protocol". – Martin R Mar 11 '18 at 16:16

2 Answers2

9

Objective-C and Swift have different name resolution schemes which cause this to happen.

  1. In Objective-C, class and protocol names are generally unique. (You can always register a new protocol or class with the same name as an existing one, but you'll generally get a warning and things are guaranteed to behave strangely.) However, class names and protocol names exist in different namespaces — class names can shadow protocol names, and vice versa. This is generally okay because classes and protocols are referred to differently in source code: a class named Foo is referred to by the bare identifier Foo, while a protocol named Foo is referred to by @protocol(Foo). There's no clashing here.
  2. In Swift, however, there is no difference in the name resolution between different types. Protocol names are in the same namespace as all other type names, and there is no difference in the syntax between referring to a class named Foo and a protocol named Foo, partially leading to the error above.

Note that because of how name resolution happens in Swift, enums/structs/classes can have the same names as protocols, and vice versa; names themselves are not unique in Swift, but fully-qualified names are. The reason you get the error above is actually because both struct Test and protocol Test would have the same fully-qualified name: <name-of-your-module>.Test

There is nothing preventing you from declaring struct Test and protocol Test in different modules, though, since they'd have different fully-qualified names. For instance, you're welcome to add

struct ExpressibleByStringLiteral {}

to your code, despite a protocol by that name being offered by the standard library. ExpressibleByStringLiteral would then shadow other usages of the identifier, so to refer to the protocol provided by the stdlib, you'd need to use the fully-qualified name of Swift.ExpressibleByStringLiteral:

struct ExpressibleByStringLiteral {}
struct S1 : ExpressibleByStringLiteral {} // error: inheritance from non-protocol type 'ExpressibleByStringLiteral'

struct S2 : Swift.ExpressiblyByStringLiteral {} // need to add methods to satisfy the protocol

This is true for all types in Swift — multiple types can have the same name as long as their fully-qualified names are unique.

Itai Ferber
  • 28,308
  • 5
  • 77
  • 83
0

The compiler can distinguish from protocol names and class names by context. So if you know where to use it as a protocol and where to use it as a class, there is no ambiguity.

Let's say we have a Car class which conforms to Car protocol.

@protocol Car <NSObject>
- (BOOL)canRun;
@end

@interface Car : NSObject <Car>
@end

@implementation Car
- (NSString *)description {
    return [NSString stringWithFormat:@"im a car"];
}
- (BOOL)canRun {
    return YES;
}
@end

You can use Car as protocol, and the compiler will know it.

/* compiler tasks Car as a protocol*/
// 1
id<Car> car;

// 2
[obj comformsToProtocol: @protocol(Car)];

// 3
@interface Truck : NSObject <Car>
@end

// 4
@protocol AnotherProtocol <Car>
@end

You can also use Car as a class and the compiler will know it also.

/* compiler tasks Car as a class*/
// 1 
Car *car = [Car new];

// 2
[Car someClassMethod];

// 3
@interface Truck : Car
@end
karlbsm
  • 347
  • 4
  • 9