使用未导入包中的类型的返回值

Given the following three go packages (I abbreviated the import paths for imp and Tdef for privacy reasons)

package main

import (
        "imp"
        "fmt"
)

func main() {
        T := imp.NewT()
        fmt.Printf("T.X = %d
", T.X)
        fmt.Printf("T has type %T
", T)
}
package imp

import (
        "Tdef"
)

func NewT() Tdef.T {
        return Tdef.T{0,0}
}
package Tdef

type T struct {
        X int
        Y int
}

func (T T) GetX() int {
        return T.X
}

main() produces the output

T.X = 0
T has type Tdef.T

This means that I can use a variable of type T in a package where the definition of T is not visible by defining it using a return value, and I can use its exported fields (and methods, not shown here).

I find that a bit surprising and did not find any information on this in the spec. Then again, I don't have much programming experience -- is this to be expected, and is it bad practice to make use of it (I don't have a real-life example right now, mind you)?

You are allowed to use values originating from anywhere, but without the import declaration you can't refer to its type for example. Yes, because referring to identifiers requires a qualified identifier which has the form of:

QualifiedIdent = PackageName "." identifier .

You haven't imported Tdef in your main package, so you can't refer to the type Tdef.T identifier (type), but you didn't attempt it so all is good.

Spec: Import declarations:

An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.

You used a short variable declaration and so you did not specify the variable's type (which is perfectly OK), so you didn't need to refer to its type:

T := imp.NewT()

T's type will be inferred from the right-hand side expression, which will be Tdef.T of course, but your source code contains no reference to this type. Obviously the following line would give a compile-time error without importing Tdef:

var T Tdef.T = imp.NewT()

You can even return values of unexported types from an exported function, and the package importing it can as well use it, e.g. it can print it, call exported methods and access exported fields of the value of the unexported type (although returning values of unexported types is considered bad practice, golint gives a warning like: "exported func XXX returns unexported type pkgname.typename, which can be annoying to use").