近段时间编程语言开始往安全方面进行发展,类型安全,内存安全,并发安全等,如新兴的Rust语言,Swift 6.0都在安全性上发力,并都在编译器方面下功夫,添加了诸多规则,当你和编译器战斗通过后,理论上你的代码具备某种程度的安全性。

这类安全性同时也影响着语言的发展,比如在面向接口编程中Swift语言里面对于接口还增加了some和any两个关键字,一时让我疑惑不解,其他语言没有这类概念为啥这个语言需要,他想要解决了什么问题?然后又引入两个类型概念Opaque Types和Boxed Protocol Types。

官方文档描述如下:

“You can think of an opaque type like being the reverse of a generic type. … An opaque type lets the function implementation pick the type for the value it returns in a way that’s abstracted away from the code that calls the function. … An opaque type refers to one specific type, although the caller of the function isn’t able to see which type”

看的云里雾里不知道要干嘛,这些描述不是就是接口要达到的效果吗,让模块外调用者不用知道模块内部细节类型,通过接口抽象进行调用。经过一番探索发现其实Opaque Types是让编译器增加了一层保护机制,由编译器进行检查让返回的接口类型保持底层类型的一致性。这样说还是太抽象举个例子就明白了。

protocol Fighter: Equatable {}
struct XWing: Fighter {}
struct YWing: Fighter {}

func lanchXFighter() -> some Fighter {return XWing()}
func lanchYFighter() -> some Fighter {return YWing()}

func compareFighter() {    
  let x = lanchXFighter()    
  let x2 = lanchXFighter()    
  let y = lanchYFighter()   

  if x == x2 {       
   print("x == y") {

   }

  //do not compile  
  if x == y {
  }
}

这里我们写两个方法通过接口返回这两个不同的类型,因为这两个类型实现的同样的接口Fighter。compareFighter方法想通过接口类型去比较前两个方法返回的类型是否一致。 在lanchXFighter返回增加了some关键字表示返回的是Opaque类型,因此编译器会记录实际的底层类型用于检查。 在compareFighter通过返回的接口类型比较的时候,如果接口的底层类型不一致,那么在编译阶段就会不通过,比如上面代码想比较“x == y”,因为x的底层类型是XWing,y的底层类型是YWing。所以在编译时期进行检查不通过。 这个就是some关键字和Opaque带来的编译时期的安全性。

那么其他语言是怎么设计的呢?比如简洁的Go语言没有这个关键字。同样的代码如下:

func lanchXFighter() Fighter {  
  return XWing{}
}
func lanchYFighter() Fighter {  
  return YWing{}
}

func compareFighter() {  
  x := lanchXFighter()  
  x1 := lanchXFighter()  
  y := lanchYFighter()

  if x == x1 {    
    fmt.Println("x == x1")  
  }

  if x == y {    
    fmt.Println("x == y")  
    }
}

Go语言设计并没提供编译时期底层类型的检查,编译直接通过,在运行期如果类型不一致比较直接返回不一致。

个人不喜欢编译时期检查过多,原本思路很清晰的代码,结果编译还报错,你还要花时间费劲和编译器斗争,最后编译通过,证明了你的代码是安全的。同时为了帮助编译器检查增加过多的关键字,也增加了程序员认知负担。作为程序员应该具备写清晰代码的能力,自己去保证比如底层类型的一致性等问题,这样才能写出更加流畅清晰的代码逻辑。