无法转换具有匹配类型的类型的值 - 我遇到了编译器限制吗?
Cannot convert value of type with matching types - have I hit a compiler limitation?
背景:
我正在构建一个带有服务层的应用程序,该服务层应该 return 模型相似(例如,所有模型都有 text: String
属性,在协议 TextModel
).该服务拥有一个存储库,用于查找 returns 符合 TextModel
的具体类型模型。存储库需要保留其内部工作的具体类型信息。我想让服务不受模型的具体类型影响,这样我就不必为每种模型类型重复它。编译器不允许我这样做...
问题:
我在 Playground 中简化的以下代码将无法编译:
enum Result<T> {
case success(T)
case error
}
// Model Layer
protocol TextModel {
var text: String { get }
}
struct Person: TextModel {
let text: String
}
// Service Layer
class TextModelService {
let repository: TextModelRepositoryType
init(repository: TextModelRepositoryType) {
self.repository = repository
}
func find(completion: @escaping (Result<TextModel>) -> ()) {
repository.find(completion: completion)
}
}
// Repository Layer
protocol TextModelRepositoryType {
func find(completion: @escaping (Result<TextModel>) -> ())
}
protocol PersonRepositoryType {
func findPerson(completion: @escaping (Result<Person>) -> ())
}
class PersonRepository: PersonRepositoryType, TextModelRepositoryType {
func find(completion: @escaping (Result<TextModel>) -> ()) {
// ERROR HERE: "Cannot convert value of type '(Result<TextModel>) -> ()' to expected argument type '(Result<Person>) -> ()'"
findPerson(completion: completion)
}
func findPerson(completion: @escaping (Result<Person>) -> ()) {
let person = Person(text: "Adam")
completion(.success(person))
}
}
let repository = PersonRepository()
let service = TextModelService(repository: repository)
service.find { result in
// result should be a Result<Person>
}
我不明白为什么编译器声明它 'Cannot convert value of type '(Result<TextModel>) -> ()' to expected argument type '(Result<Person>) -> ()'
当 Person
绝对符合 TextModel
...
同样有趣的是,当我删除 Result
类型并在完成时仅传递一个未包装的类型(例如 (TextModel?) -> ()
)时,编译器没有任何疑虑。
我是否遇到了 Swift 编译器的限制?或者我错过了什么?
因为Swift有不变的泛型类型,所以即使A
可以转换成B
,T<A>
也不能转换成T<B>
。
这种情况下的解决方法是自己创建转换方法:
enum Result<T> {
case success(T)
case error
func cast<R>() -> Result<R>? {
switch self {
case .error: return .error
case .success(let t) where t is R: return .success(t as! R)
default: return nil
}
}
}
extension Result where T : TextModel {
func convertToTextModel() -> Result<TextModel> {
switch self {
case .error: return .error
case .success(let t): return .success(t)
}
}
}
然后在find
中调用这些转换方法:
func find(completion: @escaping (Result<TextModel>) -> ()) {
findPerson(completion: { completion([=11=].convertToTextModel()) })
}
在来电方:
let repository = PersonRepository()
let service = TextModelService(repository: repository)
service.find { result in
if let person: Result<Person> = result.cast() {
// ...
}
}
背景:
我正在构建一个带有服务层的应用程序,该服务层应该 return 模型相似(例如,所有模型都有 text: String
属性,在协议 TextModel
).该服务拥有一个存储库,用于查找 returns 符合 TextModel
的具体类型模型。存储库需要保留其内部工作的具体类型信息。我想让服务不受模型的具体类型影响,这样我就不必为每种模型类型重复它。编译器不允许我这样做...
问题:
我在 Playground 中简化的以下代码将无法编译:
enum Result<T> {
case success(T)
case error
}
// Model Layer
protocol TextModel {
var text: String { get }
}
struct Person: TextModel {
let text: String
}
// Service Layer
class TextModelService {
let repository: TextModelRepositoryType
init(repository: TextModelRepositoryType) {
self.repository = repository
}
func find(completion: @escaping (Result<TextModel>) -> ()) {
repository.find(completion: completion)
}
}
// Repository Layer
protocol TextModelRepositoryType {
func find(completion: @escaping (Result<TextModel>) -> ())
}
protocol PersonRepositoryType {
func findPerson(completion: @escaping (Result<Person>) -> ())
}
class PersonRepository: PersonRepositoryType, TextModelRepositoryType {
func find(completion: @escaping (Result<TextModel>) -> ()) {
// ERROR HERE: "Cannot convert value of type '(Result<TextModel>) -> ()' to expected argument type '(Result<Person>) -> ()'"
findPerson(completion: completion)
}
func findPerson(completion: @escaping (Result<Person>) -> ()) {
let person = Person(text: "Adam")
completion(.success(person))
}
}
let repository = PersonRepository()
let service = TextModelService(repository: repository)
service.find { result in
// result should be a Result<Person>
}
我不明白为什么编译器声明它 'Cannot convert value of type '(Result<TextModel>) -> ()' to expected argument type '(Result<Person>) -> ()'
当 Person
绝对符合 TextModel
...
同样有趣的是,当我删除 Result
类型并在完成时仅传递一个未包装的类型(例如 (TextModel?) -> ()
)时,编译器没有任何疑虑。
我是否遇到了 Swift 编译器的限制?或者我错过了什么?
因为Swift有不变的泛型类型,所以即使A
可以转换成B
,T<A>
也不能转换成T<B>
。
这种情况下的解决方法是自己创建转换方法:
enum Result<T> {
case success(T)
case error
func cast<R>() -> Result<R>? {
switch self {
case .error: return .error
case .success(let t) where t is R: return .success(t as! R)
default: return nil
}
}
}
extension Result where T : TextModel {
func convertToTextModel() -> Result<TextModel> {
switch self {
case .error: return .error
case .success(let t): return .success(t)
}
}
}
然后在find
中调用这些转换方法:
func find(completion: @escaping (Result<TextModel>) -> ()) {
findPerson(completion: { completion([=11=].convertToTextModel()) })
}
在来电方:
let repository = PersonRepository()
let service = TextModelService(repository: repository)
service.find { result in
if let person: Result<Person> = result.cast() {
// ...
}
}