所有権
文字列や配列を自作関数へ渡すと、そのデータの所有権が関数へ移ります。これをmoveと呼びます。渡した変数をもう一度使うとエラーになります。数値などのコピーできる値は、そのまま繰り返し使えます。
def use_name(name: str):
print(name)
def main():
name = "alice"
use_name(name)
# print(name) はmove後の使用
このページの目次
classのフィールドを取り出す
文字列のフィールドを別の変数へ代入すると、その文字列の所有権が移ります。移動したフィールドは再利用できませんが、移動していない別のフィールドは使えます。
class Person:
name: str
age: i64
def main():
person = Person(name="Nagi", age=1)
name = person.name
print(name)
print(person.age)
# print(person.name) はmove後の使用
元のフィールドも残したい場合は、上の代入をname = copy(view(person.name))に変更します。コピーはmoveする前に作ってください。printやlenで読み取るだけなら、文字列はmoveしません。
関数に読み取り用の値を渡す例は、読むだけならviewで借りるにあります。
ループで同じ値を使う
ループの外で作った文字列を、毎回所有値として渡すと、1周目でmoveして次の周回で使えなくなります。checkはこの再利用を拒否します。フィールドを取り出す場合や、whileの条件で値を渡す場合も同じです。
元の値を残して渡すには、周回ごとにコピーを作ります。読むだけの関数なら、引数をview[str]にしてview(name)を渡せます。
def use_name(name: str):
print(name)
def main():
name = "Nagi"
for number in range(2):
use_name(copy(view(name)))
print(name)
次の周回までに新しい値を代入する方法もあります。
def use_name(name: str):
print(name)
def main():
name = "first"
for number in range(2):
use_name(name)
name = "next"
print(name)
分岐で再代入する場合は、次の周回へ進むすべての経路で値を用意してください。returnで関数を終える経路は、次の周回や後続の処理へ影響しません。ループ内で新しく作った値は周回ごとに使えます。
検査は実行回数を計算せず、繰り返しと0回の実行を考慮します。そのため、ループ内の代入だけでは、ループ後に値が使えるとは限りません。whileの条件は、ループを抜けるときにも評価されます。
借用と検査の範囲
for value in valuesは、走査する配列を借用します。次の周回へ進む経路では、その配列のappend・再代入・moveをcheckで拒否します。view(values)や、そのviewを変数に入れて走査する場合も同じです。別の配列は変更でき、ループが終了すれば走査の借用は終わります。
def main():
values = [1, 2]
output: List[i64] = []
for value in values:
append(output, value * 2)
append(values, 3)
フィールドの借用は場所ごとに追跡します。例えばview(data.values)が生きていても、別のdata.nameを取り出せます。data全体やdata.valuesの移動はできません。
viewが借りているデータの移動・再代入・appendも制限します。借用はコードのブロックをもとに追跡します。Rustほど細かく、借用を使い終わった位置を判定するわけではありません。
フィールドの一部を移す場合や、複雑な分岐・借用では、Nagiの検査だけでは判定しきれないことがあります。生成したRustでも借用を検査し、両方を通ったコードを実行ファイルにします。そのため、checkが成功してもbuildで失敗する場合があります。
元のデータとは別に保持したいときは、copy(view(data))で所有するコピーを作ります。