func mutates(_ x: UnsafeMutablePointer<Int>) { ... }
func doesntMutate(_ x: UnsafePointer<Int>) { ... }
func bar(x: Int, y: Int) {
mutates(&x) // 🛑 Cannot pass immutable value as inout argument:
// 'x' is a 'let' constant 😀👍
var x = x
mutates(&x) // ✅ 😀👍
doesntMutate(&y) // 🛑 same error?! 😒👎
var y = y // feels odd I need this 😒👎
doesntMutate(&y) // ✅
}
I would understand the error if "doesntMutate" was taking "UnsafeMutablePointer" but here it takes "UnsafePointer" and it is not possible to modify the passed argument anyway. It feels that I have to make a copy for no good reason, which might be trivial for Int, but for String / Array / etc there's a not so trivial cost even taking into account the COW optimisation (and some value types won't have COW optimisation!).
The & operator is not a pointer-to operator, it's an inout operator, and separately there's an inout-to-pointer conversion that converts an inout parameter to a mutable pointer. There's a valid argument to be made that Swift should add this capability, but you can accomplish what you want using the built-in withUnsafePointer function.
withUnsafePointer(to: y) { pointerToY in
doesntMutate(pointerToY)
}
With SE-377 we could mark the to parameter of withUnsafePointer with borrowing, but borrowing is already the implicit ownership for that parameter. If internally withUnsafePointer does something that requires a copy of its parameter then that's unfortunate.
As an optimization the compiler may sometimes pass a borrowing argument by-value when the type is copyable and simple, like Int. But because withUnsafePointer<T> is generic over its to parameter, it's always going to pass an address; the borrowing ownership just prevents the caller from having to retain/copy the value before the address is formed.
I do not think it is a good idea to allow implicit casts of &var to an unsafe type like UnsafeMutablePointer.
rust is aware of both mutable and immutable references, so passing let references is possible as immutable references, not sure why Swift is not yet on the same level of references discernment , however simply blocking immutable inout seems more like a design pitfall rather than a deliberate constraint
fn hold_my_vec<T>(_: &Vec<T>) {}
fn main() {
let v = vec![2, 3, 5, 7];
hold_my_vec(&v); // immutable borrow reference
}
UPDATE
can pass immutable references with borrowing