the following might describe a lot of programming tasks:
create an instance of TypeA that does some work on some buffers, then
transfer the buffers from TypeA to a second type, TypeB that does some more work on those buffers.
struct TypeA:~Copyable
{
private
var a:[A]
private
var b:[B]
private
var c:[C]
private
var d:[D]
private
let aStuff:Stuff
...
}
extension TypeA
{
public consuming
func finalizeMesh() -> TypeB
}
public
struct TypeB:~Copyable
{
public
let a:[A]
public
let b:[B]
public
let vertices:[Vertex]
init(
a:consuming [A],
b:consuming [B],
c:borrowing [C],
d:borrowing [D])
}
and yet i keep finding myself declaring reams of local bindings to try and pass the data from TypeA to TypeB.
extension TypeA
{
public consuming
func finalizeMesh() -> TypeB
{
let a:[A] = self.a
let b:[B] = self.b
let c:[C] = self.c
let d:[D] = self.d
_ = consume self
return .init(a: a, b: b, c: c, d: d)
}
}
extension TypeB
{
init(
a:consuming [A],
b:consuming [B],
c:borrowing [C],
d:borrowing [D])
{
var a:[A] = a
var b:[B] = b
let c:[C] = c
let d:[D] = d
for a:A in a { ... }
for b:B in b { ... }
for a:A in a { ... }
...
}
}
to me, this feels like a lot of ceremony just to accomplish a simple goal: transfer ownership of some buffers to TypeB so it can perform some post-processing.
this essentially just shifts the problem from having spurious bindings to having spurious types that exist solely for the purpose of evading ownership errors.
ideally, i would want to be able to just do
extension TypeA
{
public consuming
func finalizeMesh() -> TypeB
{
let a:[A] = consume self.a
let b:[B] = consume self.b
return .init(a: a, b: b, c: self.c, d: self.d)
}
}
and then from TypeB, to do something along the lines of
extension TypeB
{
init(
a:__owned var [A],
b:__owned var [B],
c:__shared [C],
d:__shared [D])
{
where __owned var transfers ownership but doesn’t actually make a and b non-moveable in the body of the function.
as for the init side, i’m not too annoyed with having to rebind consuming parameters, since you would have to rebind an equivalent __owned parameter anyway to make it mutable. but i find that borrowing just isn’t very useful to me when it isn’t attached to a real noncopyable parameter.
most of the time when i use borrowing, i am really just using it as a non-underscored version of __shared that comes with a lot of annoying move checking that i don’t want. is __shared still the recommended way for expressing “i want this argument passed +0, but i don’t want any move checking applied inside the function body”?