How to define a generic type's Access Level?

fileprivate class someGenerics<T> where T: Equatable {
    private var type:T?
    func get(type: T) {
    }
    
    func someFunc<U>(unit: U) {
        
    }
}
private T // error

If you mean the access level of a generic parameter, T, that is not possible. But it is also senseless, since generic parameters in universal types (generic types) are supposed to be provided by the consumer upon instantiation. Could you share what you're trying to achieve by doing so, so we can help you?

If you want some members of the class to be generic, but not the class itself, use generics on those members, i.e.

class Foo {
  func get<T>(type: T) where T: Equatable {...}
}
1 Like

I don't understand the description of access control of generic in the language guide.could you write a demo for below?

Generics
The access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any type constraints on its type parameters.

It means that if your, let's say, function is less accessible than the types used in its generic requirements, the access level of the function persists. Example:

public protocol P {...}

fileprivate func foo<T: P>(_ arg: T) {...}

// foo is obviously fileprivate despite P being public

The opposite case, when a type used in a generic requirement is less accessible, is simply illegal.

2 Likes

The documentation isn't clear enough about when this is true in terms of the language rules. But formally, although illegal in Swift, the statement is also correct in the case when a type used in a generic requirement is less accessible than the declaration. Naturally, you won't be able to use the declaration beyond the scope of that type's access level anyway.

Apologies for the editing, I realized it's more understandable this way.

1 Like

The docs still read the following as of WWDC 2020 and Swift 5.2:

“The access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any type constraints on its type parameters.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 5.2).” Apple Books. ‎The Swift Programming Language (Swift 5.7) on Apple Books

A quick example shows some of the weirdness that isn't fully captured in this statement:

private protocol myPrivateProtocol {
  func foo() -> Void
}

fileprivate func doTheGenericDance<T: myPrivateProtocol>(withFoo: T) -> Void {
  withFoo.foo()
}

The above compiled successfully: A case where the generic requirement is less accessible than the function's access.

So, it appears that the opposite case is not always illegal. It appears not, as there is a special exception for the relationship between private / file-private.

private at file level is equivalent to fileprivate.

2 Likes

At the file level, private and fileprivate are synonyms. EDIT: @Jon_Shier beat me to it :slight_smile:

private at file level is equivalent to fileprivate .

Well, now that's useful knowledge! Thanks!

@Jon_Shier is there a mention in The Swift Programming Language of this fact? It seems useful and should probably be there, but I didn't come across it in the Access Control section.

Not that I can see, I haven't read that book in quite a while. You can request it be added by filing a bug with Apple.

1 Like

Actually, TSPL calls out an important way in which they are not quite the same in this section:

The access control level of a type also affects the default access level of that type’s members (its properties, methods, initializers, and subscripts). If you define a type’s access level as private or file private, the default access level of its members will also be private or file private [respectively].

@Jumhyn based on this reading, are you saying the the equivalency of private to fileprivate wouldn’t necessarily hold true within a file for members of a type?

When you're dealing with explicit access control, private and fileprivate aren't equivalent in general:

struct A {
  private var x = 0
  fileprivate var y = 0
}

// Same file...
print(A().x) // Error
print(A().y) // Ok

After actually testing, though, it doesn't appear that the language behavior agrees with the excerpt I posted from TSPL:

private struct A {
  var x = 0
  private var y = 0
}

fileprivate struct B {
  var x = 0
}

print(A().x) // Ok! (I.e., x is not actually private)
print(A().y) // Error
print(B().x)

EDIT: Reported as SR-13196.

Would a simple solution to this be a built-in accessControlLevel(of:) function, similar to type(of:)?

@Jon_Shier @Jumhyn Would it be more accurate to say that at the global level of a file, fileprivate and private are equivalent, but not in the general case? That seems more accurate to me.

"file level" and "global level of a file" are the same thing.

1 Like

Ah - Yes, that does make sense :sweat_smile: Thanks again!

File scope variables are available anywhere in the file. private variables are available in their scopes only. private let and private var variables are available anywhere in the file only.

1 Like