Go الفعّالة › الطرق 10 / 16 السابق التالي

الطرق

المؤشرات مقابل القيم

كما رأينا مع ByteSize، يمكن تعريف طرق لأي نوع مُسمّى (ما عدا المؤشرات والواجهات)؛ فالمُستقبِل لا يلزم أن يكون بنية (struct).

وفي الحديث عن الشرائح أعلاه، كتبنا دالة Append. يمكننا بدلًا من ذلك تعريفها طريقةً على الشرائح. ولتنفيذ ذلك، نُعرّف أولًا نوعًا مُسمّىً نربط به الطريقة، ثم نجعل مُستقبِل الطريقة قيمةً من ذلك النوع:

type ByteSlice []byte

func (slice ByteSlice) Append(data []byte) []byte {
    // Body exactly the same as the Append function defined above.
}

وهذا ما زال يتطلّب من الطريقة أن تُرجع الشريحة المحدَّثة. ويمكننا التخلّص من هذا التردّه بإعادة تعريف الطريقة لتأخذ مؤشرًا إلى ByteSlice كمُستقبِل لها، فتستطيع الطريقة أن تكتب فوق شريحة المنادي:

func (p *ByteSlice) Append(data []byte) {
    slice := *p
    // Body as above, without the return.
    *p = slice
}

وفي الحقيقة يمكننا أن نفعل أفضل من ذلك. فإن عدّلنا دالتنا لتبدو كطريقة Write المعتادة، هكذا:

func (p *ByteSlice) Write(data []byte) (n int, err error) {
    slice := *p
    // Again as above.
    *p = slice
    return len(data), nil
}

يصبح النوع *ByteSlice عندئذٍ مُشبعًا للواجهة المعتادة io.Writer، وهذا عملي. فمثلًا يمكننا أن نطبع داخل واحدة منها:

var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)

ونمرّر عنوان ByteSlice لأن *ByteSlice وحدها تشبع io.Writer. أمّا القاعدة الخاصة بالمؤشرات مقابل القيم في المُستقبِلات فهي أن طرق القيمة يمكن نداءها على المؤشرات وعلى القيم، أما طرق المؤشر فلا يمكن نداءها إلا على المؤشرات.

وتنبع هذه القاعدة من أن طرق المؤشر تستطيع تعديل المُستقبِل؛ فإن نداءها على قيمة كانت ستجعل الدالة تستقبل نسخة من القيمة، فتُهمَل أي تعديلات تُحدث عليها. ولذلك تمنع اللغة هذا الخطأ. غير أن هناك استثناءً عمليًا: فحين تكون القيمة قابلة لأخذ عنوان (addressable) تتولّى اللغة الحالة الشائعة — نداء طريقة مؤشِّر على قيمة — بإدراج معامل العنونة تلقائيًا. وفي مثالنا، المتغيّر b قابل لأخذ عنوان، ومن ثمّ يمكننا نداء طريقة Write الخاصة به بمجرّد b.Write؛ إذ يعيد المُصرِّف كتابتها إلى (&b).Write نيابةً عنا.

وبالمناسبة، فإن فكرة استعمال Write على شريحة بايت هي جوهر تنفيذ bytes.Buffer.