惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Security @ Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
AWS News Blog
AWS News Blog
G
Google Developers Blog
L
Lohrmann on Cybersecurity
T
Tenable Blog
D
DataBreaches.Net
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
博客园 - 司徒正美
L
LINUX DO - 热门话题
C
Cisco Blogs
GbyAI
GbyAI
月光博客
月光博客
U
Unit 42
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
LINUX DO - 最新话题
Cisco Talos Blog
Cisco Talos Blog
C
CERT Recently Published Vulnerability Notes
博客园_首页
小众软件
小众软件
H
Hacker News: Front Page
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
博客园 - 【当耐特】
Help Net Security
Help Net Security
Cloudbric
Cloudbric
T
Tailwind CSS Blog
A
About on SuperTechFans
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
N
Netflix TechBlog - Medium
C
Cyber Attacks, Cyber Crime and Cyber Security
爱范儿
爱范儿
Martin Fowler
Martin Fowler
I
InfoQ
Blog — PlanetScale
Blog — PlanetScale
B
Blog RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Y
Y Combinator Blog
Spread Privacy
Spread Privacy
Attack and Defense Labs
Attack and Defense Labs
Recent Announcements
Recent Announcements
S
Securelist
美团技术团队
Hugging Face - Blog
Hugging Face - Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Struct Embedding in Go: Composition That Bites When You Reach for Inheritance
Gabriel Anhaia · 2026-06-14 · via DEV Community

You come to Go from a language with classes. You see struct
embedding for the first time, and it reads like inheritance. A field
with no name, methods that "carry over" to the outer type, a base
struct that your type extends. So you write code the way you always
have, and most of it works. Then a method does something you did not
ask for, a type satisfies an interface you never meant to implement,
or two embedded types fight over a name and the compiler shrugs until
the exact line that calls it.

Embedding is not inheritance. It is composition with a syntax that
promotes methods and fields up one level. Once you hold that
distinction, the surprises stop being surprises. Here is where they
come from.

Embedding promotes, it does not subclass

Write an embedded field by giving a type with no field name:

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "vroom"
}

type Car struct {
    Engine // embedded
    Brand  string
}

Car now has a Start method and a Horsepower field, both
promoted from Engine. You can write car.Start() and
car.Horsepower as if they were declared on Car.

car := Car{Engine: Engine{Horsepower: 300}, Brand: "Fiat"}
fmt.Println(car.Start())      // vroom
fmt.Println(car.Horsepower)   // 300

This is where the inheritance illusion starts. car.Start() is
sugar. The compiler rewrites it to car.Engine.Start(). The
receiver of Start is still an Engine, never a Car. There is no
base class, no super, no virtual dispatch. Engine does not know
Car exists.

That last point is the one that bites. A promoted method runs against
the embedded value, not the outer struct.

The method that ignores the outer struct

Say you want a stringer on the embedded type that reads outer fields.
This is the move that feels like overriding a base method.

type Base struct {
    Name string
}

func (b Base) Describe() string {
    return "base: " + b.Name
}

type User struct {
    Base
    Role string
}

You construct a User, set the role, and call Describe, expecting
the role to show up somehow.

u := User{Base: Base{Name: "ana"}, Role: "admin"}
fmt.Println(u.Describe()) // base: ana

There is no path from Describe to Role. Describe has a Base
receiver. Base has no idea what Role is. In a class hierarchy a
method on the parent can be overridden by the child and dynamic
dispatch picks the override. Embedding has no dispatch. If you want
User to describe itself, you declare the method on User:

func (u User) Describe() string {
    return "user: " + u.Name + " (" + u.Role + ")"
}

Now User.Describe shadows the promoted Base.Describe. Calling
u.Describe() runs the User version. Calling u.Base.Describe()
still runs the base one. Both exist. You chose which by the selector,
not by runtime type.

Accidental interface satisfaction

This is the surprise that ships to production. Embedding a type drags
its whole method set into yours, and that method set can satisfy
interfaces you never intended to implement.

type Closer interface {
    Close() error
}

type Conn struct{}

func (c *Conn) Close() error {
    fmt.Println("closing real connection")
    return nil
}

type Service struct {
    *Conn // embedded for the Query method, say
}

You embedded *Conn because you wanted its query helpers. But
*Conn has Close, so *Service now satisfies Closer too. A
pool that ranges over Closer values and calls Close will close
your service's underlying connection, possibly one shared across the
program.

func closeAll(cs []Closer) {
    for _, c := range cs {
        c.Close()
    }
}

// *Service slides into []Closer with no warning,
// and closeAll shuts its connection.

Nothing in the type declaration says "I am a Closer." The compiler
infers it from the promoted method set. If you did not mean to expose
Close, embedding was the wrong tool. Use a named field instead:

type Service struct {
    conn *Conn
}

func (s *Service) Query(q string) error {
    return s.conn.query(q)
}

Now *Service exposes only what you wrote. Close stays private to
the connection. The rule: embedding is a public promise. Everything
the embedded type exports, your type exports too.

Pointer vs value embedding changes the method set

Whether you embed Conn or *Conn decides which methods get
promoted, because of how Go builds method sets.

A method with a pointer receiver belongs to the method set of the
pointer type, not the value type. So if Conn has func (c *Conn)
Close()
, then:

  • Embedding Conn (value) into Service promotes Close only when you hold a *Service, because Close needs an addressable receiver.
  • Embedding *Conn (pointer) into Service promotes Close onto both Service and *Service.
type Service struct {
    Conn // value embed
}

var s Service
var _ Closer = &s // works: &s is addressable, Close promoted
var _ Closer = s  // compile error: Service does not implement Closer

The fix when you want the value type itself to satisfy the interface
is to embed the pointer, or to keep all receivers as values. Mixing
value and pointer receivers on the same type is where this gets hard
to reason about. Pick one receiver style per type and the method-set
rules stop fighting you.

Name collisions compile until you call them

Embed two types that both export the same method or field name, and
the outer struct compiles. The conflict is only an error at the
selector that uses the ambiguous name.

type Reader struct{}

func (Reader) Read() string { return "read" }

type Writer struct{}

func (Writer) Read() string { return "also read" }

type Pipe struct {
    Reader
    Writer
}

Pipe declares fine. You can construct it, store it, pass it around.
The Go spec says a name promoted from two embedded types at the same
depth is not promoted at all. So Pipe has no Read method.

var p Pipe
p.Read() // compile error: ambiguous selector p.Read

The collision sleeps until a caller writes p.Read(). If no caller
ever does, the ambiguity stays invisible. Then someone adds a call,
or assigns Pipe to an interface that needs Read, and the build
breaks far from where the embedding lives.

Depth matters too. A name at a shallower embed level wins over a
deeper one. So this resolves with no error:

type WriterHolder struct {
    Writer // Read sits at depth 2 from Pipe
}

type Pipe struct {
    Reader       // depth 1
    WriterHolder // depth 2 path to Writer.Read
}

The shallow Reader.Read is promoted; the deeper one is shadowed.
The asymmetry between "same depth is an error" and "different depth
silently picks the shallow one" is exactly the kind of rule that
reads fine and behaves surprisingly.

Resolve a real collision by promoting explicitly:

func (p Pipe) Read() string {
    return p.Reader.Read()
}

When embedding is the right call

None of this means avoid embedding. It means use it for what it is:
composition where you genuinely want the embedded type's surface to
become part of yours.

Embedding fits when:

  • You want to extend a type's behavior and expose its methods on purpose, like wrapping http.ResponseWriter to add logging while forwarding everything else.
  • You are building a mixin of cross-cutting methods, like an embedded sync.Mutex so s.Lock() reads cleanly. Watch the value-copy trap there: embed it and use pointer receivers.
  • You implement an interface by embedding it and overriding one method, the standard decorator shape.
type loggingWriter struct {
    http.ResponseWriter // promote the whole interface
    status int
}

func (w *loggingWriter) WriteHeader(code int) {
    w.status = code
    w.ResponseWriter.WriteHeader(code) // forward
}

That is embedding doing its job. You wanted the full
ResponseWriter surface, and you overrode one method by shadowing.

Reach for a named field instead when you want the inner type's data
without its public method set, when you do not want to promise the
inner type's interfaces, or when "has-a" describes the relationship
better than "is-a-surface-of." Most domain types fall here. A
User has an Address; it does not want every Address method
showing up on User.

The one rule

Before you embed, ask one question: do I want every exported method
and field of this type to become part of my type's public surface,
and to satisfy whatever interfaces that surface satisfies? If yes,
embed. If you only want the data, or only some behavior, give it a
field name and forward what you need by hand. The extra lines are
cheaper than an accidental Closer.


Embedding is one of those Go features that looks like a port of a
class concept and turns out to be its own thing with its own rules.
The Complete Guide to Go Programming walks through method sets,
promotion, and interface satisfaction the way the spec defines them,
so the surprises in this post become the behavior you expect.
Hexagonal Architecture in Go shows where composition over
inheritance pays off when you are wiring ports and adapters.

Thinking in Go — the 2-book series on Go programming and hexagonal architecture