LINUX.ORG.RU

Еще один сахар. Подумал это будет интересно.

 


0

4
func processFile() error {
    try {
        ptr := getPtr(fromSomeObject)        
        
	filePtr, ok := (any)(ptr).(*MyFile)

        header, err := header.Read(filePtr)
        data, err := data.Read(filePtr)
		
 	a, errA := operationA()
    	b, errB := operationB()
    	c, errC := operationC()
    

		// catch в любом порядке

		catch errA, errB, errC  {
			return fmt.Errorf("processFile operation error")
		}

		catch err {
			switch  {
			case errors.Is(err, header.ErrorCRC):
				log.Printf("Header err")
			case errors.Is(err, data.ErrorCRC):				
				log.Printf("Deader err")
			default:				
				return fmt.Errorf("processFile file error: %w", err)
			}        
			return fmt.Errorf("processFile file error: %w", err)
    	        }

                catchif !ok {
			panic("ptr is`nt MyFile")	            
                }        
		catchif ptr == nil {
			panic("nil ptr")	
		}
    }

    return err             
}
Ответ на: комментарий от LongLiveUbuntu

вербозность iferr имеет положительную сторону - код читаешь в буквальном смысле, а не угадываешь «что хотел сказать автор». никогда не испытывал зуда от этого. но это мой опыт. видел людей у которых пердак полыхает )

ergo ★★★
()
Ответ на: комментарий от LongLiveUbuntu

По конструктивнее, если не сложенно?

Выглядит и читается вроде просто. Фактический каждый catch это if err != nil. Фишка в том что его можно разместить где угодно в блоке try.

alnkapa
() автор топика
Ответ на: комментарий от alnkapa

Обработка не нужна. Компилятор добавляет после строчки с восклицательным знаком что-то вроде

if err != nil {
  return fmt.Errorf("myfile.go:123: %w", err);
}

Т.е. формирует некоторый аналог стек-трейса. Этого достаточно в 99% случаев.

Для оставшегося 1% случаев код пишется так же, как сейчас, с непосредственной обработкой ошибки по месту.

vbr ★★★★★
()
Ответ на: комментарий от alnkapa

Потому, что у разработчиков го свой путь, построенный на отрицании успешных и зарекомендовавших себя практик из других языков программирования.

Где-то это уместно. Но эргономика кода с обработкой ошибок ощутимо проигрывает традиционному подходу с исключениями.

Впрочем это всего лишь моё мнение.

vbr ★★★★★
()
Ответ на: комментарий от vbr

Вот как надо:

a, !err := operationA()

Если operationA возвращает ошибку, то происходит возврат из функции и значение err не может быть использовано далее в этой функции, а если ошибки нет, то далее по коду err всегда будет равен nil, зачем тогда объявлять символ err?

No ★★
()
Последнее исправление: No (всего исправлений: 1)
Ответ на: комментарий от No

зачем тогда объявлять символ err?

Потому, что синтаксис a := operationA() в Go уже имеет конкретное значение «игнорировать значение err» и поменять его практически невозможно.

Можешь переписать, как a := try! operationA() или в любом другом виде. Мне больше нравится мой вариант, т.к. с одной стороны он вполне понятно читается, а с другой стороны однозначно парсится. Ну а то, что переменная будет всегда иметь значение nil - ну и ладно.

Дополню, что хотя есть общепринятые способы возвращать ошибку одним последним значением, это не значит, что все обязаны этому способу следовать. Плюс - может быть не только err но и логическая ok, например. Мой вариант универсально расширяется до любых сигнатур возвращаемых значений. К примеру !ok, len := someStrangeRead().

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от LongLiveUbuntu

Одобряю и поддерживаю, но пока не очень, да.

а фишка в том что оно никогда и не будет «очень». Я уже давно понял, что в go надо писать if err != nil {} и не выеживаться.

Что например произойдет, если мы решили отрефакторить код и продолжить его выполнение даже в случае errA? В случае с iferr мы просто уберем какой-нибудь return err из этого ифа, два нажатия на кнопку в виме и тривиальный дифф в одну строчку. В коде же из поста нам сначала придется искать catch errA (который, как написано, может быть в любом порядке), а потом добавлять тот же самый if errA != nil {}. Ну и какой тогда был смысл его убирать?

Сложность рефакторинга - это та самая причина, по которой растоманы не могут написать ничего нового на своем недоязыке и либо дергают биндинги, либо переписывают уже давно существующие проекты.

Lrrr ★★★★★
()
Ответ на: комментарий от Lrrr

Сложность рефакторинга - это та самая причина, по которой растоманы не могут написать ничего нового на своем недоязыке и либо дергают биндинги, либо переписывают уже давно существующие проекты

Читал такое, что Раст наоборот всё время заставляет код рефакторить в процессе написания. Или это оно и есть?

LongLiveUbuntu ★★★★★
()

хочешь свой go с преферансом и пионерками ?…. кодогенерация тебе в помощь, а это все ненужно! Дисциплина кода должна быть в голове,а не в синтаксисе языка

fpastush
()
Последнее исправление: fpastush (всего исправлений: 1)
Ответ на: комментарий от vbr

Но эргономика кода с обработкой ошибок ощутимо проигрывает традиционному подходу с исключениями.

Чего ж там традиционного в исключениях? Просто людей так научили. Люди в процессе учёбы перепрыгнули через ассемблеры, вот и все традиции.

Так и не смог уговорить своих Шарпистов смотреть на код возврата из процедуры БД. Хотят исключение, хоть тресни. Пришлось влепить RAISE. Теперь у них там в коде фэншуй. Зато в процедурах БД каша.

Toxo2 ★★★★
()
Последнее исправление: Toxo2 (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

Еще одна попытка уйти от каскада iferr.

А смысл? Свалить все вызовы возвращающие error в одну кучу, чтобы во второй куче разгребать все возможные ошибочные кейсы? За такое надо ногами бить, по голове.

bdrbt
()
Ответ на: комментарий от Toxo2

Чего ж там традиционного в исключениях?

Года с 90-го во всех популярных языках высокого уровня были исключения. С++, JS, Python, Perl, Ruby, Java, C#. Мне вообще сложно вспомнить хоть один популярный язык до Go/Rust, которые бы использовали возврат ошибок.

Так и не смог уговорить своих Шарпистов смотреть на код возврата из процедуры БД. Хотят исключение, хоть тресни. Пришлось влепить RAISE. Теперь у них там в коде фэншуй. Зато в процедурах БД каша.

Поздравляю, у тебя в команде разумные люди.

vbr ★★★★★
()
Ответ на: комментарий от fpastush

Это долго блин. Вот такой однострочник

func (u *Group) DeepEqual(in *Group) bool {
	return u.Uuid.Equal(&in.Uuid) && u.Name.Equal(&in.Name) && u.Description.Equal(&in.Description) && u.UpdatedAt.Equal(&in.UpdatedAt)
}

Делает вот такая монстра!

func genFunDeepEqual(typeName string, fieldNames []SpecField) ast.Decl {
	results := &ast.FieldList{
		List: []*ast.Field{
			{
				Type: ast.NewIdent("bool"),
			},
		},
	}
	in := ast.NewIdent("in")
	params := &ast.FieldList{
		List: []*ast.Field{
			{
				Names: []*ast.Ident{in},
				Type: &ast.StarExpr{
					X: ast.NewIdent(typeName),
				},
			},
		},
	}
	conditions := (func() []ast.Expr {
		ret := make([]ast.Expr, 0, len(fieldNames))
		for _, v := range fieldNames {
			if v.IsDeepEqual {
				ret = append(ret, &ast.CallExpr{
					Fun: &ast.SelectorExpr{
						Sel: ast.NewIdent("DeepEqual"),
						X: &ast.SelectorExpr{
							Sel: ast.NewIdent(v.FieldName),
							X:   uLit,
						},
					},
					Args: []ast.Expr{
						&ast.UnaryExpr{
							Op: token.AND,
							X: &ast.SelectorExpr{
								Sel: ast.NewIdent(v.FieldName),
								X:   in,
							},
						},
					},
				},
				)
			} else if v.IsEqual {
				ret = append(ret, &ast.CallExpr{
					Fun: &ast.SelectorExpr{
						Sel: ast.NewIdent("Equal"),
						X: &ast.SelectorExpr{
							Sel: ast.NewIdent(v.FieldName),
							X:   uLit,
						},
					},
					Args: []ast.Expr{
						&ast.UnaryExpr{
							Op: token.AND,
							X: &ast.SelectorExpr{
								Sel: ast.NewIdent(v.FieldName),
								X:   in,
							},
						},
					},
				},
				)
			}
		}
		return ret
	})()
	var retExpr []ast.Stmt
	var combinedExpr ast.Expr
	if len(conditions) > 0 {
		combinedExpr = conditions[0]
		for _, cond := range conditions[1:] {
			combinedExpr = &ast.BinaryExpr{
				X:  combinedExpr,
				Op: token.LAND, // Логический оператор &&
				Y:  cond,
			}
		}
		retExpr = append(retExpr,
			&ast.ReturnStmt{
				Results: []ast.Expr{
					combinedExpr,
				},
			},
		)
	} else {
		retExpr = append(retExpr,
			&ast.ReturnStmt{
				Results: []ast.Expr{
					ast.NewIdent("false"),
				},
			},
		)
	}
	body := &ast.BlockStmt{
		List: retExpr,
	}
	return genFu(typeName, "DeepEqual", params, results, body)
}

Времени уходит на это много, а так да, когда очень надо то приходится.

alnkapa
() автор топика