Безопасно ли возвращать и изменять примитивные значения во время возврата функции?

(вопрос изменен на основе обсуждения, исходный вопрос ниже)

Спецификация языка Go дает примеры, показывающие, что порядок оценки примитивов не указан в отношении вызовов функций во время назначения срезов и карт. Однако ни в одном из примеров не упоминается многозначный возврат, поэтому я не уверен, применимы ли они в этом случае или нет.

Примеры из спецификации

a := 1
f := func() int { a++; return a }
x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified

Используя язык спецификации, эта функция возвращает также неуказанный:

func run() (int, int) {
    a := 1
    f := func() int { a++; return a }
    return a, f() // always return 1,2 OR always returns 2,2 OR can return either?
}

Если порядок оценки не указан, то неигрушечный пример, такой как следующий, может сломаться в будущем, если компилятор будет обновлен:

func CountRows(ctx context.Context, db *pgxpool.Pool) (int, error) {
  row := db.QueryRow(ctx, "SELECT COUNT(*)")
  var count int
  return count, row.Scan(&count)
}

Оригинальный вопрос

Мне неясно, четко ли спецификация языка go определяет, «возвращаются» ли значения, возвращаемые из funcs, по одному или после оценки всех выражений.

Ака этот код гарантированно всегда выводит 10 <nil> (как это происходит на игровой площадке) или он может когда-нибудь выводить 0 <nil>?

package main

import "fmt"

func main() {
    fmt.Println(run())
    // Output: 10 <nil>
}

func run() (int, error) {
    var i int
    return i, inc(&i)
}

func inc(i *int) error {
    *i = *i + 10
    return nil
}

Редактировать

Этот вопрос, связанный с, предполагает, что порядок оценки примитивного возвращаемого значения не указан в спецификации.

Я имел в виду всегда, независимо от версии/поставщика компилятора. Я обновлю вопрос, чтобы быть более конкретным

Matt 16.02.2023 17:20

В моей голове компилятор мог интерпретировать возврат как ret1 := i; ret2 := inc(&i); return ret1, ret2, что привело бы к 0 <nil> и было неоднозначно для меня. В спецификации говорится, что порядок оценки примитивов не определен, и приводятся примеры структур и карт, но не операторы возврата, поэтому мне неясно, охватывает ли спецификация этот случай.

Matt 16.02.2023 17:33

Вы запутываете свой вопрос, это гораздо более простой случай, показывающий порядок оценки go.dev/play/p/3m7dyhmqx-Y. Оценка отдельных выражений операндов происходит слева направо, и только затем вы оцениваете Println (или оператор return в вашем случае).

JimB 16.02.2023 17:42

Я не понимаю, чем ваш пример отличается от "неуказанных" примеров в спецификациях go.dev/play/p/NG-mBzlZFLc - конкретно я не знаю, так же ли "неуказанные" примеры в спецификации применяются к операторам возврата

Matt 16.02.2023 17:57

Я просто говорю, что ваша первоначальная структура вопроса затрудняла понимание, нет «во время возврата», и вопрос об оценке по сравнению с назначением мог бы быть более ясным. Измените аргументы на присваивание, и вы увидите, что правая сторона оценивается первой go.dev/play/p/zuNvsiN0j1K. Если вы все еще считаете, что спецификация здесь неясна, список рассылки и система отслеживания проблем — лучшие места для обсуждения.

JimB 16.02.2023 18:11

Извиняюсь за неясный вопрос, я не собирался спрашивать о порядке справа и слева, а о надежности порядка выполнения аргументов справа в многозначном возврате. В спецификации перечислены только примеры, в которых порядок не указан для среза и карты, и я не был уверен, что он также не указан для примитивов взамен.

Matt 16.02.2023 19:32

JimB 16.02.2023 23:13
Руководство для начинающих по веб-разработке на React.js
Руководство для начинающих по веб-разработке на React.js
Веб-разработка - это захватывающая и постоянно меняющаяся область, которая постоянно развивается благодаря новым технологиям и тенденциям. Одним из...
Разница между Angular и React
Разница между Angular и React
React и AngularJS - это два самых популярных фреймворка для веб-разработки. Оба фреймворка имеют свои уникальные особенности и преимущества, которые...
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
0
7
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Да! Это действительно отличный вопрос.

Другой способ написать этот код:

package main

import "fmt"

func main() {
    fmt.Println(run())
    // Output: 10 <nil>
}

func run() (int, error) {
    var i int
    err := inc(&i)
    return i, err
}

func inc(i *int) error {
    *i = *i + 10
    return nil
}

По той же причине вы можете сделать следующее

func main() {
    callIt()
}

func callIt() (int, error) {
    return multiReturn()
}

func multiReturn() (int, error) {
    return 0, nil
}

Кроме того, вы также можете сделать это, что может быть полезно, если вы оборачиваете транзакцию db (например) в обработчик ошибок.

func main() {
    result, err := assignInReturn()
    if err != nil {
        panic(err)
    }

    // the result here, assuming no error occurred, will be 1...
    fmt.Println(result)
}

func assignInReturn() (int, error) {
    var i int
    return i, wrapErrFunc(func() error {
        // db call
        if err := dbCall(); err != nil {
            return err
        }

        i = 1 // ...because of this. This will set `i` to 1 before `assignInReturn` returns to its caller
        return nil
    })
}

func wrapErrFunc(fn func() error) error {
    return fn()
}

func dbCall() error {
    // db query
    return nil
}

В этих ситуациях вы можете быть уверены, что элементы верхнего уровня return будут оцениваться до того, как будут возвращены вызывающему объекту.

Ответ принят как подходящий
  1. "Эта функция возвращает также неуказанный?"

Да. Как вы обнаружили, язык определяет порядок оценки для некоторых вещей:

при оценке операндов выражения, оператора присваивания или возврата все вызовы функций, вызовы методов и операции связи оцениваются в лексическом порядке слева направо.

Естественно, все, что не является вызовом функции, вызовом метода или операцией связи, остается неопределенным в отношении порядка вычисления в выражении, операторе присваивания или возврата.

  1. «не игрушечный пример, подобный следующему, может сломаться в будущем, если компилятор будет обновлен»

Да. Даже если компилятор не обновляется. Если вы ожидаете, что он будет иметь какой-либо конкретный результат, который зависит от порядка вычисления, тогда код уже сломан в том смысле, что язык не говорит, что код будет делать то, что вы думаете.

  1. «являются значениями, возвращаемыми из функций, «возвращаемых» по одному или после того, как все выражения вычислены».

Такого различия нет.

  1. «Гарантированно ли, что этот код всегда выводит 10 <nil> (как это происходит на игровой площадке) или он вообще может выводить 0 <nil>

Да, он может выводить 0, <nil>. По сути, это то же самое, что и в предыдущем примере run, где теперь замыкание f было преобразовано в функцию с именем inc.

Спасибо за прямые и лаконичные ответы на мои вопросы

Matt 17.02.2023 09:25

Другие вопросы по теме