Проверьте правильность дескриптора файла - PullRequest
0 голосов
/ 11 октября 2019

Есть ли способ проверить в Go, допустим ли дескриптор файла (после начальной операции открытия)?

Рассмотрим следующий фрагмент кода, который открывает файл отладки, если он указан в флагах командной строки

    upstreamDebug := flag.String("upstream_debug", "", "File for debugging")
    flag.Parse()

    var upstreamDebugFile os.File
    if *upstreamDebug != "" {
        upstreamDebugFile, err := os.OpenFile(*upstreamDebug, os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {
            log.Fatal(err)
        }
        log.Println("writing to", upstreamDebugFile.Name())
        defer upstreamDebugFile.Close()
    }

Пока все хорошо. Но позже мы захотим записать в upstreamDebugFile, если и только если это допустимый дескриптор - за пределами этого условия if {..}.

Есть ли способ проверить его на достоверность? Кажется, в lib нет встроенного метода. Сравнение с nil

    if upstreamDebugFile == nil {

дает ошибку

cannot convert nil to type os.File

1 Ответ

1 голос
/ 12 октября 2019

За комментарий:

Я использую значение upstreamDebugFile в качестве io.WriteCloser. Я считаю, что он не дает равенства с nil, даже если файл не установлен.

io.WriteCloser является экземпляром интерфейса . Между тем, os.OpenFile возвращает *os.File, что является указателем на struct. Этот реализует интерфейс, но не является значением типа этого интерфейса. Это не составляет большого труда для некоторых применений, потому что тот факт, что он реализует интерфейс, означает, что вы можете сохранить значение *os.File в io.WriteCloser.

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

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var iface io.WriteCloser
    p, err := os.OpenFile("/bad/foo", os.O_CREATE, 0666)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("before assignment, iface = %#v, and iface == nil => %t\n", iface, iface == nil)
    iface = p
    fmt.Printf("after assignment, iface = %#v, and iface == nil => %t\n", iface, iface == nil)
    fmt.Printf("meanwhile p == nil => %t\n", p == nil)
}

Go Playground output:

open /bad/foo: No such file or directory
before assignment, iface = <nil>, and iface == nil => true
after assignment, iface = (*os.File)(nil), and iface == nil => false
meanwhile p == nil => true

Как только iface имеет назначенный тип , вы не можете просто проверить его целиком. Вы должны начать тестировать его фактическое значение, его базовый тип, как в этой программе:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var iface io.WriteCloser
    p, err := os.OpenFile("/bad/foo", os.O_CREATE, 0666)
    if err != nil {
        fmt.Println(err)
    }
    iface = p
    fmt.Printf("iface.(*os.File) == nil => %t\n", iface.(*os.File) == nil)
}

Попробуйте его на игровой площадке Go. Но учтите, что если iface никогда не былоприсваивая значение типа *os.File, утверждение типа не удастся!

Если то, что вы собираетесь сохранить, всегда будет *os.File, просто объявите вашу переменную *os.File впереди:

var foo *os.File
if somecond {
    var err error
    foo, err = os.OpenFile(... arguments ...)
    ...
}
// later:
if foo != nil {
    ...
}

Обратите внимание, что вы также можете сделать это:

var iface io.WriteCloser
...
if somecond {
    p, err := os.OpenFile(...)
    ...
    if p != nil {
        iface = p
    }
}

и теперь iface == nil снова достаточно. Но если вы действительно не планируете использовать несколько различных базовых io.WriteCloser реализаций, оставайтесь простыми и просто используйте *os.File напрямую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...